char *strncpy(char *dest, const char *src, size_t n) *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)=25memset(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. 这就可能会导致性能问题,这也是我

网上很多博客也写了这个函数,面试也常常会遇到,但是,我发现网上的很多代码都是有问题的,我们先看下大部分网上博客的实现:

[plain] view plain copy

  1. char *strncpy(char *dst, const char *src, size_t len)
  2. {
  3. assert(dst != NULL && src != NULL);
  4. char *res = dst;
  5. while (len--)
  6. {
  7. *dst++ = *src++;
  8. }
  9. return res;
  10. }

看着好像没啥问题,但是,当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,因此,正确的代码应该如下:

[plain] view plain copy

  1. char *strncpy(char *dest, const char *src, size_t len)
  2. {
  3. assert(dest != NULL && src != NULL);
  4. char *res = dest;
  5. int offset = 0;
  6. if (strlen(src) < len)//src长度小于len
  7. {
  8. offset = len - strlen(src);
  9. len = strlen(src);
  10. }
  11. while (len--)
  12. {
  13. *dest++ = *src++;
  14. }
  15. while (offset--)
  16. {
  17. *dest++ = '\0';
  18. }
  19. return res;
  20. }

使用这个函数,尤其需要注意,不要出现len>strlen(dst)的情况,如果len>strlen(dst),那么会破坏dst后面的内存:

我们假设前面红色部分是dst,然后strncpy(dst,src,10);那么后面黄色部分的内存就被破坏了。strncpy是不负责检测len是否大于dst长度的。

总的来说,strncpy总是复制len个字符到dst指向的内存!!!

所以,还会出现下面的情况:

[plain] view plain copy

  1. char message[] = "abcd";
  2. strncpy(message, "abcde",5);
  3. cout << message;

输出是abcde烫烫烫烫烫烫烫烫烫烫烫烫烫烫  (结果不唯一)

message的内存是有5个字节的,但是将abcde拷贝过去时,最后面的‘\0’被覆盖了,strncpy并不会负责添加‘\0’到dst结尾,因此,输出该字符串是,会在e字符后面一直找到‘\0’才结束,因此就会出现乱码。

考虑内存重叠

        char message[] = "qwertyu";printf("sizeof(message)=%d\n",sizeof(message)); //sizeof(message)=8strncpy(message,"abc",3);printf("message=%s\n",message); //message=abcrtyuchar *p = NULL;p=(char*)malloc(100);memcpy(p,"123456789",strlen("123456789")); //会等到错误的结果,有一个长度参数,只能拷>贝cnt个字节就结束了printf("before p =%s\n",p); //before p =123456789strcpy(p+1,p); //注意:这里重叠了,而strcpy是根据判断原串中的'\0'printf("after p =%s\n",p);//after p =1123456789free(p);return(0);
}

面试中经常会遇到让你写一个能够处理内存重叠的strncpy,标准库中的strncpy是不考虑内存重叠的,如果出现内存重叠,结果将是未定义的。

网上的很多博客也有这个代码的实现,其实很多也是有问题的,没有考虑src长度小于len的问题:

[plain] view plain copy

  1. char *strncpy(char *dst, const char *src, size_t len)
  2. {
  3. assert(dst != NULL && src != NULL);
  4. char *res = dst;
  5. if (dst >= src && dst <= src + len - 1)//重叠,从后向前复制
  6. {
  7. dst = dst + len - 1;
  8. src = src + len - 1;
  9. while (len--)
  10. *dst-- = *src--;
  11. }
  12. else
  13. {
  14. while (len--)
  15. *dst++ = *src++;
  16. }
  17. return res;
  18. }

那么,如果要处理内存重叠,该怎么办?如果内存重叠和src的长度小于len这两种情况同时出现,又如何处理?

见图,假设红色部分为src,黄色为dst。如果出现内存重叠,我们很容易想到:从后往前拷贝。如果src的长度小于len,则在后面补NUL。

[plain] view plain copy

  1. char *strncpy(char *dst, const char *src, size_t len)
  2. {
  3. assert(dst != NULL && src != NULL);
  4. char *res = dst;
  5. int offset = 0;
  6. char *tmp;
  7. if (strlen(src) < len)//src长度小于len
  8. {
  9. offset = len - strlen(src);
  10. len = strlen(src);
  11. }
  12. if (dst >= src && dst <= src + len - 1)//重叠,从后向前复制
  13. {
  14. dst = dst + len - 1;
  15. src = src + len - 1;
  16. tmp = dst;
  17. while (len--)
  18. *dst-- = *src--;
  19. }
  20. else
  21. {
  22. while (len--)
  23. *dst++ = *src++;
  24. tmp = dst;
  25. }
  26. while (offset--)
  27. {
  28. *tmp++ = '\0';
  29. }
  30. return res;
  31. }

那么,如果len的值大于dst的值,就会破坏dst后面的内存空间,这应该是要避免的。

最后,我们看一个有意思的东西:(此处strncpy是考虑内存重叠的版本)

message的长度增加了0.0  当然  ,它后面的内存被破坏了,这可能带来严重的后果。

最后,使用strncpy时,最好自动添加‘\0’在结尾:

[plain] view plain copy

  1. char buffer[BSIZE];
  2. .
  3. .
  4. strncpy(buffer,name,BSIZE);
  5. buffer[BSIZE-1]='\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函数对内存重叠做了处理。

memmove用于从src拷贝count个字节到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。但复制后src内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。

#include <string.h>
#include <stdio.h>
int main()
{char s[] = "Golden Global View";memmove(s,s+7,strlen(s)+1-7);printf("%s\n",s); //Global Viewreturn 0;
}

函数原型:

1

2

void *memcpy( void *dest, const void *src, size_t count );

void *memmove( void* dest, const void* src, size_t count );

 1.memcpy和memmove相同点
都是用于从src拷贝count个字节到dest。

2.memcpy和memmove区别
如果目标区域和源区域有重叠的话:
memcpy不能够确保源串所在重叠区域在拷贝之前被覆盖。
memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后src内容会被更改,当目标区域与源区域没有重叠则和memcpy函数功能相同。

但当源内存和目标内存存在重叠时,memcpy会出现错误,而memmove能正确地实施拷贝,但这也增加了一点点开销。

memmove的处理措施:
(1)当源内存的首地址等于目标内存的首地址时,不进行任何拷贝
(2)当源内存的首地址大于目标内存的首地址时,实行正向拷贝
(3)当源内存的首地址小于目标内存的首地址时,实行反向拷贝

3.Linux下的实现过程
linux下,两个函数都在头文件string.h中定义,函数原型为:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

void * __cdecl memcpy ( void * dst,const void * src,size_t count);

void * __cdecl memmove ( void * dst,const void * src,size_t count);

//实现代码如下:

void * __cdecl memcpy ( void * dst,const void * src,size_t count){

         void * ret = dst;

         while (count--) {

        // 注意, memcpy函数没有处理dst和src区域是否重叠的问题

                   *(char *)dst = *(char *)src;

                   dst = (char *)dst + 1;

                   src = (char *)src + 1;

         }

         return(ret);

}

 

void * __cdecl memmove ( void * dst,const void * src,size_t count){

         void * ret = dst;

         if (dst <= src || (char *)dst >= ((char *)src + count)) {

                   // 若dst和src区域没有重叠,则从起始处开始逐一拷贝

                   while (count--){

                            *(char *)dst = *(char *)src;

                            dst = (char *)dst + 1;

                            src = (char *)src + 1;

                   }

         }

         else{

        // 若dst和src 区域交叉,则从尾部开始向起始位置拷贝,这样可以避免数据冲突

                   dst = (char *)dst + count - 1;

                   src = (char *)src + count - 1;

                   while (count--){

                            *(char *)dst = *(char *)src;

                            dst = (char *)dst - 1;

                            src = (char *)src - 1;

                   }

         }

         return(ret);

}

当src和dst区域没有重叠时,两个函数是完全一样的。否则,memcpy不能正常工作的,memmove是可以正常工作的。

4.Windows平台

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

#include <stdio.h>

#include <string.h>

/* VS2010, Windows XP, Debug模式下运行 */

int main(void)

{  

    void test_memfunc(void);

    test_memfunc();

    return 0;

}

void test_memfunc(void){

    char s1[] = "abcdefgefghijklmnopq";//首地址:0x0012ff48

    char s2[] = "123456789";//首地址:0x0012ff34

    char *c = NULL;

    int l = sizeof(s1);//数组s1长度为21

     /*

     内存重叠 : s2覆盖了原s1()的一部分空间。

     即: s2(0x0012ff34-0x0012ff49)   的内存地址范围和s1(0x0012ff48-0x0012ff5c)的内存地址范围发生重叠

     */

     c = memcpy(s2,s1,sizeof(s1));//改用memmove同样运行出错

     /*

     运行出错:变量s1损坏。

     Run-Time Check Failure #2 - Stack around the variable 's2' was corrupted.

     */

     printf("%s",s1);

}

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

函数 —— strncpy() (内存重叠) memcpy() memmove() 一个字符串拷贝给另一个字符串相关推荐

  1. C语言程序设计 | 模拟实现内存操作函数:strncpy, strncat, strncmp, memcpy, memmove

    模拟实现字符串和内存操作函数(二)目录: strncpy strncat strncmp strcmp memcpy memmove strncpy char* strncpy(char* dst, ...

  2. linux拷贝文件夹到另一台机器,linux肿么一个文件拷贝到另一个文件夹

    chmod 是这样用的... chmod 700 B 就行了 4=write 2=read 1=execute 瞎说... 权限是安位计算的 700代表3类权限 第一个数字是用户权限 第2个数字是组权 ...

  3. strcpy,strncpy,memcpy,memmove

    ❤️强烈推荐人工智能学习网站❤️ 这4个字符串处理函数是我们经常用到的,下面我们来了解释一下它们之间的区别. strcpy:只用于字符串的复制,以\0为结束符,且\0也一并拷贝过去.不需要在末尾加上\ ...

  4. 十一.字符串函数与内存函数

    目录 一.求字符串长度 二.长度不受限制的字符串函数 1.strcpy函数 2.strcat函数 3.strcmp函数 三.长度受限制的字符串函数 1.strncpy函数 2.strncat函数 3. ...

  5. memcpy内存重叠的解决

    内存重叠:拷贝的目的地址在源地址范围内.所谓内存重叠就是拷贝的目的地址和源地址有重叠. 在函数strcpy和函数memcpy都没有对内存重叠做处理的,使用这两个函数的时候只有程序员自己保证源地址和目标 ...

  6. memcpy内存重叠问题

    memcpy内存重叠 之前一直没有注意到内存复制函数的内存重叠问题.今天偶遇遂琢磨了一下,记之. 函数简介:c和c++使用的内存拷贝函数,memcpy函数的功能是从源src所指的内存地址的起始位置开始 ...

  7. C语言函数:内存函数memmove()以及实现与使用。

    C语言函数:内存函数memmove()以及实现与使用. memmove(): 头文件:#include <string.h> 函数参数:          可以发现memmove()函数的 ...

  8. c语言拷贝特定个数的字符串,C语言strncpy函数

    C语言strncpy函数教程 strncpy 是一种 C 语言的标准库函数,在拷贝时,我们可以指定最多复制 n 个字符.当源字符串的长度小于 n 时,目的字符串的剩余部分将用空字节填充. strncp ...

  9. C语言之字符串探究(五):N系列字符串函数——strncpy、strncat、strncmp

    相关博文:C++之char和string字符串类探究 相关博文:C语言之数组探究(一):定义.大小.初始化.访问和三要素 相关博文:C语言之字符串探究(一):字符串与字符数组 相关博文:C语言之字符串 ...

最新文章

  1. hdu 1054(最小顶点覆盖)
  2. golang orm 框架之 gorm
  3. 全球及中国甜菊糖苏打水行业营销模式与盈利前景调研报告2022版
  4. 小程序获取用户信息_App自评估指南:小程序也可参考,第三方获取信息需获用户授权...
  5. shiro学习(1):shiro简介
  6. XMLHttpRequest+WebForm模式(接口IHttpHandler)实现ajax
  7. 日记 2014-5-18
  8. 【LeetCode】231. Power of Two
  9. LVS_DR实现过程...
  10. RS信号制java程序,java – 如何部署一个JAX-RS应用程序?
  11. php propel,关于propel--PHP
  12. r语言plotmds_多元统计分析R语言建模| 11 多维标度法MDS
  13. Linux 基础命令3 shell
  14. 神奇技术:科学家借助AI从受害人脑中还原犯罪者样貌
  15. Python基础学习----异常
  16. .编写一个文件加解密程序,通过命令行完成加解 密工作
  17. java工程师优秀简历模板,这原因我服了
  18. gem5中的O3 Pipeline Viewer Visualization实现方法
  19. 详解搜索引擎的高级搜索语法指令
  20. Js逆向:建筑市场监管平台

热门文章

  1. AppStreamMgr
  2. 聊聊微服务的服务注册与发现
  3. NET4.0.X中的状态机工作流
  4. 互联网工作原理(17.网络电视是如何工作的)
  5. 图像识别中距离变换的原理及作用详解,并附用OpenCV中的distanceTransform实现距离变换的代码
  6. mysql 拷贝数据库 表存在却打不开_mysql数据库文件复制后表打不开
  7. overlay网络的优势
  8. 静态链接和动态链接优缺点
  9. linux基本知识点学习
  10. flash动画制作成品_Flash制作点燃蛋糕蜡烛的互动动画