學過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
