| 函數 | 輸出目標 | 參數形式 | 備註 |
|---|---|---|---|
printf | stdout | 可變參數 | |
fprintf | FILE* | 可變參數 | |
sprintf | char* buf | 可變參數 | 不安全,無長度限制 |
snprintf | char* buf | 可變參數 + size | 安全,限制寫入長度 |
vprintf | stdout | va_list | |
vfprintf | FILE* | va_list | |
vsprintf | char* buf | va_list | 不安全 |
vsnprintf | char* buf | va_list + size | 安全 |
\0)。snprintf/vsnprintf 特殊:若緩衝區太小導致截斷,返回**本應寫入的完整長度**(不含 \0),而非實際寫入數。可用於預計算所需緩衝區大小。char buf[10]; int len = snprintf(buf, 10, "%s$", "1234567890123"); // len = 13 (實際字符串長度), buf = "123456789" (9字符+\0)
sprintf 不檢查目標緩衝區大小,極易導致棧溢出、任意代碼執行漏洞。
強制規範:永遠使用 snprintf/vsnprintf,並傳入正確的 size(通常為 sizeof(buf))。
sprintf(buf, "%s append", buf); // 未定義行為!
C 標準明確規定:sprintf/snprintf/vsprintf/vsnprintf 的源字符串(參數)與目標緩衝區**重疊時行為未定義**。
正確做法:使用臨時緩衝區或 memmove 錯開內存。
printf(user_str)printf(user_input); // 危險!若 user_input 含 "%n" 可任意寫內存
%n 將已輸出字符數寫入對應指針參數。攻擊者構造 "%n%n%n" 可修改關鍵內存(如返回地址、函數指針)。
鐵律:格式化字符串必須為字面量或可信常量。正確:printf("%s", user_input);。
va_list 類型變量在 va_start 後、va_end 前有效,va_end 後狀態未定義,不可復用。char* 給 %s 時必須確保以 \0 結尾,否則越界讀取。double,%f 對應 double,float 也可用 %f(自動提升)。掌握 printf 族函數的返回值語義、緩衝區管理、重疊未定義行為、格式化字符串漏洞,是編寫安全、健壯 C 代碼的基本功。原則:默認用 snprintf,格式串硬編碼,拒絕 sprintf。