strcpy ,strncpy ,strlcpy, snprintf, memcpy 用法、區別和效率
1、 strcpy
strcpy 是依據 /0 作為結束判斷的,/0會被拷貝。如果 to 的空間不夠,則會引起 buffer overflow。strcpy 常規的實現程式碼如下(來自 OpenBSD 3.9):
char *strcpy(char *to, const char *from)
{
char *save = to;
for (; (*to = *from) != '/0'; ++from, ++to);
return(save);
}
但通常,我們的 from 都來源於使用者的輸入,很可能是非常大的一個字串,因此 strcpy 不夠安全。
2、 strncpy
在 ANSI C 中,strcpy 的安全版本是 strncpy。
char *strncpy(char *s1, const char *s2, size_t n);
把src所指由NULL結束的字串的前n個位元組複製到dest所指的陣列中。
src和dest所指記憶體區域不可以重疊且dest必須有足夠的空間來容納src的字串。
當n<=strlen(s1)時,s2是沒有結束符“\0”的。
char buf[8];
strncpy( buf, "abcdefgh", 8 );
這個程式,buf 將會被 “abcdefgh” 填滿,但卻沒有 /0 結束符。
當n>strlen(s1)時,給s2不夠數的空間裡填充“\0”
strncpy 將會把未填充的空間都用 /0 填充。這又出現了一個效率上的問題,如下:
char buf[80];
strncpy( buf, "abcdefgh", 79 );
上面的 strncpy 會填寫 79 個 char,而不僅僅是 “abcdefgh” 本身。
strncpy 的標準用法為:(手工寫上 /0)
strncpy(path, src, sizeof(path) - 1);
path[sizeof(path) - 1] = '/0';
len = strlen(path);
3、 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);
使用 strlcpy,就不需要去手動負責新增 /0 了,它會複製siz-1個字元,並填充\0。strlcpy返回的是src字串的長度,而strncpy返回的是dest的指標。
當src的長度大於等於size時,拷貝size-1個字元到dst中,並自動填充‘\0’,
而使用 strlcpy,就不需要我們去手動負責 /0 了,僅需要把 sizeof(dst) 告之 strlcpy 即可:
strlcpy(path, src, sizeof(path));
len = strlen(path);
if ( len >= sizeof(path) )
printf("src is truncated.");
並且 strlcpy 傳回的是 strlen(str),因此我們也很方便的可以判斷資料是否被截斷。
4、snprintf
int snprintf(char *str, size_t size, const char *format, ...);
通過snprintf(dst_str, dst_size, “%s”, source_str); 可以將source_str拷貝到dst_str。snprintf的特點是安全,不管怎麼著,它都能保證結果串str以\0結尾,哪怕dst_size不夠大,它都能做好截斷,同時在末尾新增上\0。
在效能方面,當source_str遠長於dst_size時,該函式卻很低效的,其他情況下該函式即安全又高效。
當source_str遠長於dst_size時,該函式低效,是因為該函式的返回值為source_str的長度,那麼它就必須將source_str全部過一遍,哪怕並不拷貝到dst_str中去。
注意,當source_str長度比dst_size小時,它不會把末尾的多餘字元置零,所以它是很高效的,不會多做無用功。
4、memcpy
void *memcpy(void *dest, const void *src, int n);
從源src所指的記憶體地址的起始位置開始拷貝n個位元組到目標dest所指的記憶體地址的起始位置中
#include < string.h >
函式返回一個指向dest的指標。
1.source和destin所指記憶體區域不能重疊,函式返回指向dest的指標。
2.與strcpy相比,memcpy並不是遇到’\0’就結束,而是一定會拷貝完n個位元組。
3.如果目標陣列dest本身已有資料,執行memcpy()後,將覆蓋原有資料(最多覆蓋n)。如果要追加資料,則每次執行memcpy後,要將目標陣列地址增加到你要追加資料的地址。
效能比較
在需要用到字串拷貝函式的時候,永遠不要使用strncpy(),無論什麼時候都用snprintf()來代替,而memcpy()是效能更好的實現方式。
歷史
strlcpy 並不屬於 ANSI C,至今也還不是標準。
strlcpy 來源於 OpenBSD 2.4,之後很多 unix-like 系統的 libc 中都加入了 strlcpy 函式,我個人在 FreeBSD、Linux 裡面都找到了 strlcpy。(Linux使用的是 glibc,
glibc裡面有 strlcpy,則所有的 Linux 版本也都應該有 strlcpy)
但 Windows 下是沒有 strlcpy 的,對應的是strcpy_s函式