前言
此文章中會整理所有在 C/C++ printf 的相關用法,包含 printf, fprintf, sprintf, snprintf,
以及 vprintf, vfprintf, vsprintf, vsnprintf,會比較他們之間的差別,以及提供程式碼範例。
我們一共會介紹這些:
- printf, fprintf, sprintf, snprintf
- vprintf, vfprintf, vsprintf, vsnprintf
先講結論
給新手、還沒有很注重「程式執行使用系統空間」的開發者結論
如果你是 C/C++ 新手,或是還沒有很注重「程式執行使用系統空間」的開發者,
這篇基本上不用看了XD,你只要記得「printf」、「fprintf」、「sprintf」的差別就好!
他們可以幫助你打天下!
如果你是好奇 v 開頭的那系列
vprintf, vfprintf, vsprintf, vsnprintf ,我們可以仔細觀察,
與 printf, fprintf, sprintf, snprintf 只差一個開頭的 「 v 」,
這個 v 代表的是使用 va_list 的意思,也就是說,我們使用了「不限定傳入參數數量的方法」。
也就是說,我們寫好了一個 function,他可以同時接受傳入 1, 2, 3…各種數量的參數。
知道這個概念即可,其他的就與原來「沒有 v」的相同。
最後, printf, fprintf, sprintf, snprintf 的快速結論
function | 主要特色 |
---|---|
prinf | 基本用法,直接顯示在 terminal 上 |
fprinf | 「檔案相關」才會用到 |
sprinf | 將值「傳入第一個變數」中 |
snprinf | 將值「傳入第一個變數」中,n代表可以控制傳入大小,作為系統空間管理較為安全 |
printf
定義
簡單來說,就是直接印出來 (在你的 terminal 上),
可以說是基本中的基本用法XD。
> printf
int printf ( const char * format, ... );
Print formatted data to stdout
Writes the C string pointed by format to the standard output (stdout).
If format includes format specifiers (subsequences beginning with %),
the additional arguments following format are formatted and inserted in the resulting string replacing their respective specifiers.
> http://www.cplusplus.com/reference/cstdio/printf/
範例程式碼 – printf
這邊直接使用官方範例,裡面其實有講到很多格式調整的部份,
但這裡就先不特別講 (太多內容XD。而且會偏題),之後會有另外一篇文特別去寫。
#include <stdio.h>
using namespace std;
void test_printf()
{
char s[] = "Hello";
printf("Strings - padding:\n");
printf("\t.%10s.\n\t.%-10s.\n\t.%*s.\n", s, s, 10, s);
printf("Strings - truncating:\n");
printf("\t%.4s\n\t%.*s\n", s, 3, s);
printf("Characters:\t%c %%\n", 65);
printf("Integers\n");
printf("Decimal:\t%i %d %.6i %i %.0i %+i %i\n", 1, 2, 3, 0, 0, 4, -4);
printf("Hexadecimal:\t%x %x %X %#x\n", 5, 10, 10, 6);
printf("Octal:\t\t%o %#o %#o\n", 10, 10, 4);
printf("Floating point\n");
printf("Rounding:\t%f %.0f %.32f\n", 1.5, 1.5, 1.3);
printf("Padding:\t%05.2f %.2f %5.2f\n", 1.5, 1.5, 1.5);
printf("Scientific:\t%E %e\n", 1.5, 1.5);
printf("Hexadecimal:\t%a %A\n", 1.5, 1.5);
}
int main()
{
printf(" ------ test_printf ------ \n");
test_printf();
return 0;
}
編譯與結果
> g++ printf_test.cpp -std=c++14 -o a.out && ./a.out
------ test_printf ------
Strings - padding:
. Hello.
.Hello .
. Hello.
Strings - truncating:
Hell
Hel
Characters: A %
Integers
Decimal: 1 2 000003 0 +4 -4
Hexadecimal: 5 a A 0x6
Octal: 12 012 04
Floating point
Rounding: 1.500000 2 1.30000000000000004440892098500626
Padding: 01.50 1.50 1.50
Scientific: 1.500000E+00 1.500000e+00
Hexadecimal: 0x1.8p+0 0X1.8P+0
fprintf
定義
f 代表的是 file,透過 fprintf 能專門幫助我們處理「寫進檔案的字串」。
> fprintf
int fprintf ( FILE * stream, const char * format, ... );
Write formatted data to stream
Writes the C string pointed by format to the stream. If format includes format specifiers (subsequences beginning with %), the additional arguments following format are formatted and inserted in the resulting string replacing their respective specifiers.
After the format parameter, the function expects at least as many additional arguments as specified by format.
> http://www.cplusplus.com/reference/cstdio/fprintf/
範例程式碼 – fprintf
我們將寫出的檔案寫在 「”file.txt”」 的檔案裡面。
#include <stdio.h>
using namespace std;
void test_fprintf()
{
FILE * fp;
fp = fopen("file.txt", "w+");
fprintf(fp, "%s %s %s %d!!!", "Hello", "world", "in", 2021);
fclose(fp);
}
int main()
{
printf(" ------ test_fprintf ------ \n");
test_fprintf();
return 0;
}
編譯與結果
> g++ printf_test.cpp -std=c++14 -o a.out && ./a.out
- 在同目錄底下的 「”file.txt”」 檔案裡面:
Hello world in 2021!!!
sprintf
定義
s 代表的是 string,透過 sprintf 能幫助我們把字串保存在變數裡面。
> sprintf
int sprintf ( char * str, const char * format, ... );
Write formatted data to string
Composes a string with the same text that would be printed if format was used on printf, but instead of being printed, the content is stored as a C string in the buffer pointed by str.
The size of the buffer should be large enough to contain the entire resulting string (see snprintf for a safer version).
A terminating null character is automatically appended after the content.
After the format parameter, the function expects at least as many additional arguments as needed for format.
> https://www.cplusplus.com/reference/cstdio/sprintf/
範例程式碼 – sprintf
以下的程式碼提供的兩種印出 string 訊息的方式:
- printf(“%s\n”, str);
- puts(str);
可以自行比較一下差別,與找尋自己喜歡的用法。
#include <stdio.h>
using namespace std;
void test_sprintf()
{
const char* s = "Hello";
sprintf(str, "%s", s);
printf("%s\n", str);
puts(str);
}
int main()
{
printf(" ------ test_sprintf ------ \n");
test_sprintf();
return 0;
}
編譯與結果
> g++ printf_test.cpp -std=c++14 -o a.out && ./a.out
------ test_sprintf ------
Hello
Hello
snprintf
定義
snprintf 與 sprintf 用絕大部分都相同,
只差在多一個 n ,代表的是 numbers of bytes 的控制,
相對於 sprintf 會更安全的控制好電腦內儲存空間的限制,
但如果還是新手,或還沒有很注重 「電腦內儲存空間的限制」的開發者,
可以先使用「sprintf」。
> snprintf
int snprintf ( char * s, size_t n, const char * format, ... );
Write formatted output to sized buffer
Composes a string with the same text that would be printed if format was used on printf, but instead of being printed, the content is stored as a C string in the buffer pointed by s (taking n as the maximum buffer capacity to fill).
If the resulting string would be longer than n-1 characters, the remaining characters are discarded and not stored, but counted for the value returned by the function.
A terminating null character is automatically appended after the content written.
After the format parameter, the function expects at least as many additional arguments as needed for format.
> http://www.cplusplus.com/reference/cstdio/snprintf/
範例程式碼 – snprintf (正常使用,感受不到他的重要性)
- 來自官方程式碼
#include <stdio.h>
using namespace std;
void test_snprintf()
{
char buffer [100];
int cx;
cx = snprintf ( buffer, 100, "The half of %d is %d", 60, 60/2 );
if (cx>=0 && cx<100) // check returned value
snprintf ( buffer+cx, 100-cx, ", and the half of that is %d.", 60/2/2 );
puts (buffer);
}
int main()
{
printf(" ------ test_snprintf ------ \n");
test_snprintf();
return 0;
}
編譯與結果
> g++ printf_test.cpp -std=c++14 -o a.out && ./a.out
------ test_snprintf ------
The half of 60 is 30, and the half of that is 15.
範例程式碼 – snprintf (不正常使用,我們才能知道他的重要性)
我們先宣告一個「 buffer[10] 」的空間給系統知道,
但我們故意存入一個15+1(含結束字元’\0′)「”abcdeabcdeabcde”」,給這個 buffer,
想當然,16個位置怎麼存得進去10個位置,
應該要有未知的問題發生。
#include <stdio.h>
using namespace std;
void test_snprintf()
{
char buffer[10];
int cx;
// not safe
// sprintf ( buffer, "abcdeabcdeabcde" );
// puts(buffer);
cx = snprintf ( buffer, 10, "abcdeabcdeabcde" );
puts(buffer);
cout << "cx = " << cx << ", strlen(buffer) = " << strlen(buffer) << endl;
// cx = 15, strlen(buffer) = 9
// abcdeabcd\0
// 0123456789, that is, *(buffer+9) = \0
}
int main()
{
printf(" ------ test_snprintf ------ \n");
test_snprintf();
return 0;
}
編譯與結果
正常使用 – snprintf
> g++ test3.cpp -std=c++14 -o a.out && ./a.out
test3.cpp: In function ‘void test_snprintf()’:
test3.cpp:7:6: warning: ‘abcdeabcdeabcde’ directive output truncated writing 15 bytes into a region of size 10 [-Wformat-truncation=]
void test_snprintf()
^~~~~~~~~~~~~
test3.cpp:15:6: note: ‘snprintf’ output 16 bytes into a destination of size 10
cx = snprintf ( buffer, 10, "abcdeabcdeabcde" );
~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
------ test_snprintf ------
abcdeabcd
cx = 15, strlen(buffer) = 9
- 可以看出:
- 程式跳出了 warning,警告空間不足的問題
- 但因為 snprintf 限制了最大大小,所以只有存入 「abcdeabcd + ‘\0’」 ,共10個字元
- 「程式有正常的執行結束!」
異常對照組 – 使用 sprintf
> g++ test3.cpp -std=c++14 -o a.out && ./a.out
test3.cpp: In function ‘void test_snprintf()’:
test3.cpp:12:11: warning: ‘void* __builtin_memcpy(void*, const void*, long unsigned int)’ writing 16 bytes into a region of size 10 overflows the destination [-Wstringop-overflow=]
sprintf ( buffer, "abcdeabcdeabcde" );
~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
------ test_snprintf ------
abcdeabcdeabcde
strlen(buffer) = 15
*** stack smashing detected ***: <unknown> terminated
[1] 10889 abort (core dumped) ./a.out
- 可以看出:
- 程式也有跳出了 warning,警告空間不足的問題
- 因為沒有特別限制儲存空間大小,所以 「abcdeabcdeabcde + ‘\0’」全部都存入了 ,共16個字元
- 「程式 “沒有” 正常的執行結束!,最後跳出了 core dumped。」
Reference
- printf、fprintf、sprintf和snprintf 区别
- printf、fprintf、sprintf和snprintf 區別
- vprintf
- printf、fprintf、sprintf和snprintf 區別
- vprintf
- printf
- va_start
- vprintf printf区别
- 不定長度引數
- vsnprintf用法解析
- printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf – 輸出格式轉換
- C中snprintf与vsnprintf函数,自定义可变参数格式化字符串
- prinf 家族:printf, fprintf, sprintf, snprintf 區別
- 官方文件: printf, fprintf, sprintf, snprintf, printf_s, fprintf_s, sprintf_s, snprintf_s
- std:string formatting like sprintf
- sprintf() – C語言庫函數
[…] https://www.wongwonggoods.com/cplusplus/cpp_string_format/cpp-printf/ […]