strncpy
的用法:它与strcpy
的不同之处就在于复制n
个字符,而不是把所有字符拷贝(包括结尾'\0'
)。
函数原型:char * strncpy(char *dst,const char * src, int n)
当src
的长度小于n
时,dst
内的未复制空间用'\0'
填充。否则,复制n
个字符到dst
,没有加'\0'
。这里就要注意在字符串dst
结尾处理加'\0'
的情况了
strcpy
,strncpy
,strlcpy
的用法好多人已经知道利用strncpy
替代strcpy
来防止缓冲区越界。但是如果还要考虑运行效率的话,也许strlcpy
是一个更好的方式。
我们知道,strcpy
是依据 \0
作为结束判断的,会自动在缓冲区的string
后加\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
不够安全。
它和strcpy()
函数的功能应该一样的。strcpy
函数,就象gets
函数一样,它没有方法来保证有效的缓冲区尺寸,所以它只能假定缓冲足够大来容纳要拷贝的字符串。在程序运行时,这将导致不可预料的行为。用strcpy_s
就可以避免这些不可预料的行为。
这个函数用两个参数、三个参数都可以,只要可以保证缓冲区大小。
三个参数时:
errno_t strcpy_s(char *strDestination, size_t numberOfElements, const char *strSource);
两个参数时:
errno_t strcpy_s(char (&strDestination)[size], const char *strSource ); // C++ only
需要注意的是如果需要使用两个参数的版本,则strDestination
所指向的空间必须时静态分配的,而不能是动态new
出出来的堆内存。
在 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 个 char,而不仅仅是 "abcdefgh"
本身。
strncpy
的标准用法为:(手工写上 \0
)
strncpy(path, src, sizeof(path) - 1);
path[sizeof(path) - 1] = '\0';
len = strlen(path);
复制src
到大小为size
目的缓冲区,最多复制siz-1
个,第size
个位置会被添加'\0'
,执行结束后会返回strlen(src)
。所以,如果retval >= siz
,那么由于目的缓冲区太小src
被截断了.
size_t strlcpy(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
传回的是 strlen(str)
,因此我们也很方便的可以判断数据是否被截断,但同时也给程序埋了一个雷,所以,strlcpy
并不属于 ANSI C
,至今也还不是标准。