➣ Reading Time: 39 minutes

前言

此文章中會整理所有在 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
  • 可以看出:
  1. 程式跳出了 warning,警告空間不足的問題
  2. 但因為 snprintf 限制了最大大小,所以只有存入 「abcdeabcd + ‘\0’」 ,共10個字元
  3. 程式有正常的執行結束!

異常對照組 – 使用 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
  • 可以看出:
  1. 程式也有跳出了 warning,警告空間不足的問題
  2. 因為沒有特別限制儲存空間大小,所以 「abcdeabcdeabcde + ‘\0’」全部都存入了 ,共16個字元
  3. 程式 “沒有” 正常的執行結束!,最後跳出了 core dumped。」

Reference

⭐C++ 基礎用法 相關文章整理⭐:
1.【C++】C++ compile 程式碼 使用 c++ 11 與使用相關的 package
2.【C++】C/C++ 顯示資料的類別 (type) sample code (內含範例程式碼) print C data type, cout C++ data type, get variable type in c++
3.【C++】C++ 複製 2D array的方法 copy 2d array memcpy sample code (內含範例程式碼)
⭐C++ 字串處理相關文章整理⭐:
1.【C++】字串 char string stringstream 相關用法總整理 (內含範例程式碼) 與利用 sprinf, snprinf assign 值的方法
2.【C++】字串 char string stringstream 「轉換」用法總整理 (內含範例程式碼)
3.【C】printf, fprintf, sprintf, snprintf 相關用法總整理 (內含範例程式碼)
4.【C++】C++ String 用法 連接兩個 String c++ string concat
⭐C++ 系統偵測相關文章整理⭐:
1.【C++】C++ 利用 dirent.h 計算資料夾的檔案數量 count files sample code (內含範例程式碼)
2.【C++】C++ inotify sample code 偵測指定路徑底下的文件變化 (內有範例程式碼)
⭐C++ OpenCV 相關文章整理⭐:
1.【OpenCV】c++ OpenCV – 在 ubuntu 上第一次執行 OpenCV 程式 sample code (內含範例程式碼)
2.【OpenCV】c++ OpenCV - 在圖片上寫上文字 cv::putText sample code (內含範例程式碼)
3.【OpenCV】c++ OpenCV - cv::Rect 矩形用法與相關功能函數 sample code (內含範例程式碼)
4.【OpenCV】c++ OpenCV - OpenCV 中的純量 定義顏色 cv::Scalar(255,255,255) color sample code (內含範例程式碼)
5.【OpenCV】用 C++ 計算 iou 的方法 與網路算法常見錯誤(內附範例程式碼) sample code
⭐【喜歡我的文章嗎? 幫忙按讚除了鼓勵外,我也會將部分所得捐出!
如果喜歡我的文章,請幫我在下方【按五下Like】 (Google, Facebook 免費註冊),會由 「LikeCoin」 贊助作者鼓勵繼續創作,扣除掉網站本身經營的成本 (可惜目前還是虧本的),我會將 【50% 收益全部捐出】 並公開發文,讀者們「只需幫忙按讚,完全不用出錢」哦!

likecoin-steps