1.不考虑内存重叠的strncpy

网上很多博客也写了这个函数,面试也常常会遇到,但是,我发现网上的很多代码都是有问题的,我们先看下大部分网上博客的实现:
[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’才结束,因此就会出现乱码。

2.考虑内存重叠的strncpy

面试中经常会遇到让你写一个能够处理内存重叠的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';

深入理解strncpy这个函数相关推荐

  1. python参数传递方法_深入理解python中函数传递参数是值传递还是引用传递

    python 的 深入理解python中函数传递参数是值传递还是引用传递 目前网络上大部分博客的结论都是这样的: Python不允许程序员选择采用传值还是传 引用.Python参数传递采用的肯定是&q ...

  2. 深入浅出理解c++虚函数

    深入浅出理解c++虚函数 记得几个月前看过C++虚函数的问题,当时其实就看懂了,最近笔试中遇到了虚函数竟然不太确定,所以还是理解的不深刻,所以想通过这篇文章来巩固下. 装逼一刻: 最近,本人思想发生了 ...

  3. 理解javascript 回调函数

    理解javascript 回调函数 原文:理解javascript 回调函数 ##回调函数定义 百度百科:回调函数 回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一 ...

  4. 深入理解C++重载函数

    深入理解C++重载函数 1.定义:C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数.类型或者顺序)必须不同,即函数的参数列表不同,也就是说用同一个运算符完成不 ...

  5. Linux底层IIC 总线的理解、调用函数以及常见面试问题

    对 IIC 总线的理解.调用函数以及常见面试问题 一.IIC 总线概述: IIC 即Inter-Integrated Circuit(集成电路总线) I2C总线是PHLIPS公司推出的一种串行总线, ...

  6. 简单理解 柯理化函数

    理解柯理化函数: 1. 是函数 2. 接受多个参数(一个参数没必要用柯理化) 3. 将 接受多个参数 的函数变成 接受一个参数 的函数(函数嵌套),每个函数返回一个新函数并接受一个新的参数,直到最后返 ...

  7. 如何理解python中的函数_如何理解“python中函数是一等公民”?

    python.js.scala等支持函数式编程的语言中,是如何体现"函数是一等公民(first class)"的?而在c/c++.java等静态语言中的一等公民又是什么?如何体现的 ...

  8. c语言 字符串 strncpy,详解c语言中的 strcpy和strncpy字符串函数使用

    详解c语言中的 strcpy和strncpy字符串函数使用 strcpy 和strcnpy函数--字符串复制函数. 1.strcpy函数 函数原型:char *strcpy(char *dst,cha ...

  9. 《C语言杂记》理解字符串处理函数 strlen() strcpy() strcat() strcmp()

    在linux C 编程中,我们经常遇到字符串的处理,最多的就是字符串的长度.拷贝字符串.比较字符串等:当然现在的C库中为我们提供了很多字符串处理函数.熟练的运用这些函数,可以减少编程工作量,这里介绍几 ...

最新文章

  1. 关于bitmap,为什么android会有bitmap
  2. 【 FPGA 】UltraFast设计方法学:在Vivado中使用设计规则检查
  3. 20.17 shell中的函数
  4. java轻量级并行工具类_16 个超级实用的 Java 工具类
  5. struts2从action向jsp传参数
  6. .xyz文件_Orca.xyz:除了银行系统瑞士还有同样安全的数字保险箱
  7. 中国计算机学会推荐国际学术期刊--数据库/数据挖掘/内容检索
  8. java指标计算_java – 使用JMH计算指标
  9. error LNK2019: 无法解析的外部符号 main,函数 “int __cdecl __scrt_common_main_seh(void)“ (?__scrt_common_main_seh
  10. 出入机房计算机无登记表,三峡大学机房维护管理制度
  11. M1 Macbook安装MATLAB
  12. 苹果Mac怎样清除dns缓存?
  13. 基于JavaEE的健身房管理系统的设计
  14. MSF-17010(永恒之蓝)复现
  15. 【ChatGPT模板】教学辅助教案篇
  16. 认识Base64,看这篇足够了
  17. 基于验证分离的PLC保护系统
  18. FreeEIM 网站地图 A
  19. 使用示波器学习变压器
  20. 安卓终端神器Termux (后面还有Termux常用的快捷键,妥妥干货分享,记得点赞收藏哦!)

热门文章

  1. restful api与传统api的区别(方式及语法)
  2. GitLab使用自定义端口
  3. xml凭证模板的一般制作
  4. jquery中$.post()方法的简单实例
  5. 从.NET和Java之争谈IT这个行业
  6. poj 1379 模拟退火法
  7. 【Android】Handler详解
  8. Jackson 框架使用说明,轻易转换JSON【转】
  9. 线程的切入和切出(切入: 一个线程被系统选中占用处理器开始或继续运行)
  10. 日常笔记-css\html篇