snprintf 和 strncpy 的細節區別
現在一般不能用 sprintf 和 strcpy ,推薦使用 snprintf 和 strncpy ,以防止緩衝區溢出:
char buffer[32]; snprintf(buffer, sizeof(buffer), "xxxxxxxxx"); strncpy(buffer, "xxxxxxx", sizeof(buffer));
但 snprintf 和 strncpy 有一些很細節的區別,一不注意就會出錯:
- 如果預複製(或打印)的字符串長度小於緩衝區長度(上面例子裏的 32 ),那麼 snprintf 會在末尾添加一個 0 ,而 strncpy 會在剩餘的空間都填上 0。
- 如果預複製(或打印)的字符串長度大於等於緩衝區長度(上面例子裏的 32 ),那麼 snprintf 會複製 31 個字符,然後填上一個 0 ,此時 buffer 是一個正常的 C 字符串,但比源字符串字符數要少。但 strncpy 會複製剛好 32 個字符,不會添加 0。此時 buffer 不是一個正常的 C 字符串,可能引起緩衝區溢出。最新的編譯器會給出警吿。
- snprintf 返回打印的字符數(不包含尾部的 0 ), strncpy 返回 buffer (貌似多此一舉)。
從防範風險的角度看, snprintf 更安全,但 snprintf 要慢很多。實際工作中需要取捨。
從 GCC9 開始,編譯器會給 strncpy 給出一個 -Wstringop-truncation
的編譯警吿,提示可能沒有 0 結束字符問題。
從實際工作角度,可以構造一個 sprintf 和 strncpy 的安全結合版:
inline char * safe_strncpy(char* dest, const char* src, size_t n) { assert(n > 0); strncpy(dest, src, n - 1); dest[n - 1] = 0; return dest; }
最後一點,如果確信源字符串長度不小於緩存區長度,可以直接用 memset
,效率更高(但注意最後沒有添加 0 ):
char buffer[8]; memcpy(buffer, "xxxxxxxxx", sizeof(buffer));
Q. E. D.
「其他文章」