1. 程式人生 > >strcpy ,strncpy ,strlcpy, snprintf, memcpy 用法、區別和效率

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函式