char *strncpy(char *dest, const char *src, size_t n)
/*功能:
* C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小>于 n 时,dest 的剩余部分将用空字节填充。*/
/*参数:
* dest -- 指向用于存储复制内容的目标数组。
* src -- 要复制的字符串。
* n -- 要从源中复制的字符数。*/
/*返回值:
* 该函数返回最终复制的字符串。*/
说明: 如果src的前n个字节不含NULL字符,则结果不会以NULL字符结束。 如果src的长度小于n个字节,则以NULL填充dest直到复制完n个字节。 src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。 返回指向dest的指针。
不考虑内存重叠
int main(void) { char src[40]; char dst[12]; char dst_less[25]; strcpy(src,"This is runoob.com"); printf("strlen(src) = %d\n",strlen(src)); //strlen(src) = 18
误解一:如果src 长度大于等于 n, 那么 strncpy 会拷贝 n – 1 各字符到 dest, 然后补 0?
错,大错特错,罚抄上面的 DESCRIPTION ,直到看到:
if there is no null byte among the first n bytes of src, the result will not be null-terminated.
这就可能导致了不安全的因素。
如果待拷贝字符串长度大于了 n, 那么 dest 是不会有结尾字符 0 的。假设这样一种情况:
1
2
3
|
char
s[] =
"hello world"
;
strncpy
(s,
"shit!"
, 5);
puts
(s);
|
输出的结果是 “shit” 还是 “shit! world” ?
这种情况只是导致了输出结果错误,严重的,如果 dest n 字节后面一直没有 0,那么就会导致程序段错误。
strncpy 最开始引入标准库是用来处理结构体中固定长度的字符串,比如路径名,而这些字符串的用法不同于 C 中带结尾字 0 的字符串。所以 strncpy 的初衷并不是一个安全的 strcpy.
//src 的长度 大于 或者 等于 len的长度(18>10) //把src所指向的字符串复制到dest,最多赋值10个,注意它的结果不会以NUL字节结尾(NUL即'\0') memset(dst,'\0',sizeof(dst)); strncpy(dst,src,10); printf("最终的目标字符串是:%s\n",dst);//最终的目标字符串是:This is ru
误解二:如果 src 长度小于 n, 那么strncpy 和 strcpy 效果一样?
错,事实上,strncpy 还会把 dest 剩下的部分全部置为 0!
//src的长度 小于 len的长度(18<20) //dest_less数组 的剩余部分将用空字节填充 到len的长度。 printf("sizeof(dst_less)=%d\n",sizeof(dst_less)); //sizeof(dst_less)=25 memset(dst_less,'\0',sizeof(dst_less)); strncpy(dst_less,src,20); printf("最终的目标字符串是: %s\n",dst_less);//最终的目标字符串是: This is runoob.com
一直认为 strncpy 只是比 strcpy 多了长度校验,确不知道 strncpy 会把剩下的部分全置为 0(粗体部分)。
char *strncpy(char *dest, const char *src, size_t n);
DESCRIPTION
The strcpy() function copies the string pointed to by src (including the terminating `\0′ character) to the array pointed to by dest. The strings may
not overlap, and the destination string dest must be large enough to receive the copy.
The strncpy() function is similar, except that not more than n bytes of src are copied. Thus, if there is no null byte among the first n bytes of src,
the result will not be null-terminated.
In the case where the length of src is less than that of n, the remainder of dest will be padded with null bytes.
这会导致什么后果呢?
首先,如果 strncpy 的长度填错了,比如比实际的长,那么就可能会把其他数据清 0 了。我就遇到过这个问题,在后来检查代码看到这个问题时,也并不以为然,因为拷贝的字符串不可能超过缓冲区的长度。
另外,假设 dest 的长度为 1024, 而待拷贝的字符串长度只有 24,strncpy 会把余下的 1000 各字节全部置为 0. 这就可能会导致性能问题,这也是我
网上很多博客也写了这个函数,面试也常常会遇到,但是,我发现网上的很多代码都是有问题的,我们先看下大部分网上博客的实现:
看着好像没啥问题,但是,当src的长度小于len呢?这份代码没有处理这个问题。当src的长度小于len时,应该如何处理?《C和指针》p179给出的答案是:
“
和strcpy一样,strncpy把源字符串的字符复制到目标数组。然而,它总是正好向dst写入len个字符。如果strlen(src)的值小于len,dst数组就用
额外的NUL字节填充到len长度,如果strlen(src)的值大于或等于len,那么只有len个字符被复制到dst中。”
注意!它的结果将不会以NUL字节结尾。(NUL即‘\0’).
由此可见,我们还需要判断strlen(src)是否小于len,如果是,还需要在dst后面添加NUL,因此,正确的代码应该如下:
使用这个函数,尤其需要注意,不要出现len>strlen(dst)的情况,如果len>strlen(dst),那么会破坏dst后面的内存:
我们假设前面红色部分是dst,然后strncpy(dst,src,10);那么后面黄色部分的内存就被破坏了。strncpy是不负责检测len是否大于dst长度的。
总的来说,strncpy总是复制len个字符到dst指向的内存!!!
所以,还会出现下面的情况:
message的内存是有5个字节的,但是将abcde拷贝过去时,最后面的‘\0’被覆盖了,strncpy并不会负责添加‘\0’到dst结尾,因此,输出该字符串是,会在e字符后面一直找到‘\0’才结束,因此就会出现乱码。 |
考虑内存重叠
char message[] = "qwertyu"; printf("sizeof(message)=%d\n",sizeof(message)); //sizeof(message)=8 strncpy(message,"abc",3); printf("message=%s\n",message); //message=abcrtyu char *p = NULL; p=(char*)malloc(100); memcpy(p,"123456789",strlen("123456789")); //会等到错误的结果,有一个长度参数,只能拷>贝cnt个字节就结束了 printf("before p =%s\n",p); //before p =123456789 strcpy(p+1,p); //注意:这里重叠了,而strcpy是根据判断原串中的'\0' printf("after p =%s\n",p);//after p =1123456789 free(p); return(0); }
面试中经常会遇到让你写一个能够处理内存重叠的strncpy,标准库中的strncpy是不考虑内存重叠的,如果出现内存重叠,结果将是未定义的。
网上的很多博客也有这个代码的实现,其实很多也是有问题的,没有考虑src长度小于len的问题:
那么,如果要处理内存重叠,该怎么办?如果内存重叠和src的长度小于len这两种情况同时出现,又如何处理?
见图,假设红色部分为src,黄色为dst。如果出现内存重叠,我们很容易想到:从后往前拷贝。如果src的长度小于len,则在后面补NUL。
那么,如果len的值大于dst的值,就会破坏dst后面的内存空间,这应该是要避免的。
最后,我们看一个有意思的东西:(此处strncpy是考虑内存重叠的版本)
message的长度增加了0.0 当然 ,它后面的内存被破坏了,这可能带来严重的后果。
最后,使用strncpy时,最好自动添加‘\0’在结尾:
|
解决问题:
int main(void) { char string[8] = {'\0'}; char *str1 = "abcde"; int i =0; strncpy(string, str1, 3); string[3] = '\0'; for(i=0;i<sizeof(string);i++) { printf("sting[%d] = %c\n",i,string[i]); } printf("%s\n", string); return 0; }
内存重叠:拷贝的目的地址在源地址范围内。所谓内存重叠就是拷贝的目的地址和源地址有重叠。
在函数strcpy和函数memcpy都没有对内存重叠做处理的,使用这两个函数的时候只有程序员自己保证源地址和目标地址不重叠,或者使用memmove函数进行内存拷贝。
memmove函数对内存重叠做了处理。
函数原型:
1.memcpy和memmove相同点
当src和dst区域没有重叠时,两个函数是完全一样的。否则,memcpy不能正常工作的,memmove是可以正常工作的。 4.Windows平台
windows平台下,当发生内存重叠的时候,都不能正常运行,运行栈被破坏,提示错误:Run-Time Check Failure #2 - Stack around the variable 's2' was corrupted. |
参考链接:
//https://blog.csdn.net/sinat_30071459/article/details/72771137//http://www.kuqin.com/clib/string/strncpy.html
//http://blog.haipo.me/?p=1065
//https://www.cnblogs.com/Benoly/p/3845000.html
//http://www.kuqin.com/clib/string/strncpy.html