安全的strlcpy和strlcat字符串操作函數


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM