學過C語言的肯定都知道strcpy和strcat,但是這兩個函數有個致命的缺陷,它們不檢查dst是否有足夠的空間,如果src足夠長必然會導致緩沖區溢出,於是有就了改進版strncpy和strncat,這兩個函數在一定程度上解決了安全問題,但是很多程序員都不願使用它們,原因如下:
1. 對'\0'的處理
size_t num參數表示需要拷貝的字符個數,在num小於等於src的情況下,strncpy只拷貝前num個字符,並不自動添加\0,如果dst沒有全部初始化為\0,輸出就會亂碼;在num大於src並且src等於dst的情況下,本來strncpy會自動在dst中添加\0,但此時dst空間已經滿了,無法再拷貝\0,因此輸出也會亂碼。
2. 效率問題
另外,假如dst空間足夠大,在src遠小於dst的情況下,使用sizeof(dst)作為num參數進行拷貝時,會多拷貝dst-src-1次\0,這是完全不需要的操作,因此有人說strncpy的有些行為是很詭異的。
因此使用strncpy時應該顯式設置\0,標准方法是這樣的:
strncpy(dst, src, sizeof(dst) - 1); dst[sizeof(dst) - 1] = '\0'; // 手動添加\0
有人說這樣寫可以避免效率問題:
strncpy(dst, src, strlen(src)); dst[strlen(src)] = '\0'; // 手動添加\0
但其實回到問題的初衷了,原因是從src計算出的字串長度無法保證小於等於dst,可能會導致緩沖區溢出,因此依然無法避免多次拷貝\0的效率問題,strlcpy和strlcat的出現很好的解決了上述兩個問題。
size_t strlcpy(char *dst, const char *src, size_t siz); size_t strlcat(char *dst, const char *src, size_t siz);
strlcpy的源碼:
/* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { register char *d = dst; register const char *s = src; register size_t n = siz; if (s == 0 || d == 0) return 0; /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { do { if ((*d++ = *s++) == 0) break; } while (--n != 0); } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ }
1. strlcpy可以自動處理\0,只需要將sizeof(dst)作為size參數即可。
2. strlcpy返回strlen(src),用於判斷src是否被截斷。
int len; len = strlcpy(dst, src, sizeof(dst)); if (len >= sizeof(dst)) printf("truncation occurred.");
strlcat的源碼:
/* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t siz) { register char *d = dst; register const char *s = src; register size_t n = siz; size_t dlen; if (s == 0 || d == 0) return 0; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ }
ps:strlcpy並不屬於ANSI C,至今也還不是標准。不過glibc加入了strlcpy函數,目前Linux發行版中都有該函數。Windows下是沒有strlcpy的,strcpy的安全版本為strcpy_s,具體查詢MSDN。
原文:http://www.gratisoft.us/todd/papers/strlcpy.html
譯文:http://blog.csdn.net/linyt/article/details/4383328
參考資料:http://www.cppblog.com/tx7do/archive/2009/10/08/98071.html