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.
「其他文章」