字符串复制函数——strcpy,strncpy和strlcpy

来源:互联网 发布:淘宝宠物链 编辑:程序博客网 时间:2024/05/16 00:55

好多人已经知道利用strncpy替代strcpy来防止缓冲区越界。

但是如果还要考虑运行效率的话,也许strlcpy是一个更好的方式。

  

1. strcpy

我们知道,strcpy 是依据\0作为结束判断的,如果 to 的空间不够,则会引起 buffer overflow。strcpy 常规的实现代码如下(来自 OpenBSD 3.9):

char * strcpy(char *to, const char *from) {        char *save = to;       for (; (*to = *from) != ”; ++from, ++to);        return(save); }

但通常,我们的 from 都来源于用户的输入,很可能是非常大的一个字符串,因此 strcpy 不够安全。

 

2. strncpy

在 ANSI C 中,strcpy 的安全版本是 strncpy。

char *strncpy(char *s1, const char *s2,size_t n);


但 strncpy 其行为是很诡异的(不符合我们的通常习惯)。标准规定 n 并不是 sizeof(s1),而是要复制的 char 的个数。一个最常见的问题,就是 strncpy 并不帮你保证字符串以\0结束。

char buf[8];strncpy( buf, "abcdefgh", 8 ); 

看这个程序,buf 将会被 "abcdefgh" 填满,但却没有\0结束符了。 

另外,如果 s2 的内容比较少,而 n 又比较大的话,strncpy 将会把之间的空间都用\0填充。这又出现了一个效率上的问题,如下:

char buf[80];strncpy( buf, "abcdefgh", 79 );

上面的 strncpy 会填写 79 个 \0,而不仅仅是 "abcdefgh" 本身。

strncpy 的标准用法为:(手工写上 )

strncpy(path, src, sizeof(path) – 1);path[sizeof(path) – 1] = 0;len = strlen(path);

3. strlcpy

官方注释:

// Copy src to string dst of size siz. Atmost siz-1 characters

// will be copied. Always NUL terminates(unless siz == 0).

// Returns strlen(src); if retval >=siz, truncation occurred.

size_tstrlcpy(char *dst, const char *src, size_t siz);

而使用 strlcpy,就不需要我们去手动负责\0了,仅需要把 sizeof(dst) 告之 strlcpy 即可:

strlcpy(path, src, sizeof(path));len = strlen(path); if ( len >= sizeof(path) )      printf("src is truncated.");

strlcpy常常是更加安全地选择,因为strlcpy在复制之后dst字符串一定会以'\0'字符结尾代码说明如下:

int main(){    char buf[5];    char src[10] = "12345678";    strncpy(buf, src, sizeof(buf));    printf("%s\n",buf);    return 0;}
这个时候buf最终结果就是"12345",并没有以结束字符结尾。而如果我们使用strlcpy,buf字符串最终结果是“1234\0”,以结束字符结尾。

因此,建议使用strlcpy。

最后给上strlcpy源码:

size_t strlcpy( char *dst, const char *src, size_t siz ){    char*            d = dst;    const char*        s = src;    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 */}

根据函数看到,strlcpy也不需要在n过大的时候进行'\0'的补齐工作。不过我们是用的时候基本上都是用的sizeof(des)。




原创粉丝点击