面试官很喜欢让求职者写一些常用库函数的实现,有很多是和字符串相关的,有一些是关于内存拷贝的。一般,常会让写的函数有以下几个:

strcpy , strncpy, memcpy。

memset一般不会让去写,但这个函数也很有特点,有很多容易用错的地方。一并总结吧。

1. strcpy

strcpy函数的原型是:

char * strcpy(char* dest, const char* src)

strcpy的实现经常要注意的细节是:

(1)判断地址是否为空,个人感觉可以使用断言

(2)参数只有两个地址,没有拷贝的长度。拷贝到'\0‘时就会终止,要保证最终dest末尾是'\0'。

(3)要保证目标字串的长度足够,能够容纳原串的长度。

(4)因为拷贝是dest会移动,而最终要返回的是拷贝后字符串的起始地址,因此要先保存dest的地址,便于最终返回。

在实现这一点时,有两种方法。 char* temp=dest; 拷贝时移动dest返回temp,或者拷贝时移动temp返回dest,不知道哪个是对的。感觉两个都是没有问题的

其中一种实现方式:

[cpp] view plaincopy
  1. char* mystrcpy(char* dest,const char* src)
  2. {
  3. assert(dest!=NULL && src!=NULL);
  4. char* temp=dest;
  5. while((*temp++ = *src++ )!='\0')
  6. {}
  7. return dest;
  8. }

2. strncpy

strncpy的功能和strcpy相似,只是它复制时多了一个终止条件。即是未遇到原串的'\0’,如果已经复制了n个字符(n为提供的参数长度),复制同样会终止。

strcpy的实现要注意的细节也基本适用于strncpy的实现。

实现方式:

[cpp] view plaincopy
  1. char* mystrncpy(char* dest, const char* src, int len)
  2. {
  3. assert(dest!=NULL && src!=NULL);
  4. char* temp=dest;
  5. int i=0;
  6. while(i++ < len  && (*temp++ = *src++)!='\0')
  7. {}
  8. if(*(temp)!='\0')
  9. *temp='\0';
  10. return dest;
  11. }

3. memcpy

memcpy和strncpy有些类似,但也有本质的不同。

(1)strncpy只能复制字符串,但memcpy对类型没有要求。

(2)strncpy有两个终止条件,memcpy只有一个终止条件,那就是复制n个字节。(n是memcpy的第三个参数)

(3)要特别注意目的地址和源地址重合的问题,拷贝前要加以判断。

(4)实现这个函数时一般要把原来的指针类型转换成char*,这样每次移动都是一个字节。

实现方式:(考虑了两个地址空间是否会有重叠)

[cpp] view plaincopy
  1. void* mymemcpy(void* dest, void* src, int len)
  2. {
  3. int i=0;
  4. char* tempdest=(char*)dest;
  5. char* tempsrc=(char*)src;
  6. if(tempdest<tempsrc || tempdest>(tempsrc+len-1))
  7. {
  8. while(i<len)
  9. {
  10. *tempdest++ = *tempsrc++;
  11. i++;
  12. }
  13. }
  14. else
  15. {
  16. tempdest+=len;
  17. tempsrc+=len;
  18. i=len;
  19. while(i>0)
  20. {
  21. *tempdest-- = *tempsrc--;
  22. i--;
  23. }
  24. }
  25. return dest;
  26. }

注意,memcpy是对内存的拷贝,对其他安全性不做考虑。用户在使用这个函数时要小心,比如用它来拷贝字符串(当然如果是字符串拷贝肯定是用strncpy)就要注意末尾的\0字符之类的。

4. memset

memset函数的原型是:

void *memset(void *s, int ch,size_t n)

作用是把s所指向的地址开始的n个字节的内容全部置位ch所指定的ASCII值。

一般经常用memset对某段内存空间置零。

经常会出现的一个问题:在C++中,为什么不提倡在构造函数中使用:memset(this,0,sizeof(*this))

原因: 在C++中,如果类中都是基本类型的数据成员并且没有虚函数和虚继承的话,使用memset这样用到没有太多影响。

如果有虚函数,memset会把虚表指针等全部置零,对类会产生破坏。

三个函数的原型如下:

[cpp] view plaincopy
  1. void* memset(void *des, int val, size_t size)
  2. void * memcpy(void *des, const void* src, size_t size)
  3. void * memmove(void *des, const void *src, size_t size)

实现如下:

[cpp] view plaincopy
  1. void* memset(void *des, int val, size_t size) {
  2. void *start = des;
  3. while (size--) {
  4. *(char*) des = (char) val;
  5. des = (char *) des + 1;
  6. //      (char*) des++;
  7. //      des = (char* )des + 1;
  8. }
  9. return start;
  10. }
  11. void * memcpy(void *des, const void* src, size_t size) {
  12. void *ret = des;
  13. while (size--) {
  14. *(char *) des = *(char *) src;
  15. des = (char *)des + 1;
  16. src = (char *)src + 1;
  17. //      (char *) des++;
  18. //      (char *) src++;
  19. }
  20. return ret;
  21. }
  22. void * memmove(void *des, const void *src, size_t size) {
  23. void *ret = des;
  24. if (des < src || (char *) des > (char *) src + size - 1) {
  25. while (size--) {
  26. *(char *) des = *(char *) src;
  27. des = (char *) des + 1;
  28. src = (char *)src + 1;
  29. //              (char *) src++;
  30. //              (chr *) des ++;
  31. }
  32. }else{
  33. des = (char *)des + size - 1;
  34. src = (char *)src + size - 1;
  35. while (size -- > 0){
  36. *(char *) des = *(char *) src;
  37. //          (char *) des--;
  38. //          (char *) src--;
  39. des = (char *)des - 1;
  40. src = (char *)src - 1;
  41. }
  42. }
  43. return ret;
  44. }

不采用//中的写法是因为包报出警告:warning: value computed is not used

看起来不爽。

注意事项:

(1)使用memset的时候,要把最后一位或者最后一位的下一位置为‘\0’;

[cpp] view plaincopy
  1. char buffer[20] = "hello";
  2. memset(buffer, '1', sizeof(char)*20);
  3. printf("%s\n",buffer);
  4. 运行结果:111111111111111111110@
  5. char buffer[20] = "hello";
  6. memset(buffer, '1', sizeof(char)*20);
  7. buffer[20] = '\0';
  8. printf("%s\n",buffer);
  9. 运行结果:11111111111111111111

因为在prinf一个字符串的时候,printf函数是遇见‘\0就停止。想第一个例子中的,buffer[20]都是‘1’,结束没有‘\0’,所以打印出来的结果就不确定。当然,也有可能是对的,那只是运气好而已。

(2)memcpy和strcpy的区别:

实际上区别只有一个,strcpy的操作对象只能是char *,而memcpy操作的对象是void *。(什么类型的都可以)。实际上,在memcpy的实现上,都是将(void *)装换成为了(char *)来做的,其实跟strcpy一样。

(3)memmove和memcpy的区别:

区别就是memmove要考虑内存区间重叠的情况,而memcpy不会。

关于这个问题,可以用下面的图片来解释:

内存区间重叠的情况如下和不会出现内存区间重叠的情况:

假设des为src + 2,如果按照memcpy处理,从头开始拷贝,就要出现下面的悲剧:

src的内存都被污染了,而且如果这时候打印*des开头的内存,仍然会出现未定义的情况:因为'\0'被覆盖了。

[cpp] view plaincopy
  1. char buffer5[10] = "1234";
  2. memcpy(buffer5 + 2, buffer5, sizeof(buffer5));
  3. printf("%s\n", buffer5);
  4. char buffer6[10] = "1234";
  5. memmove(buffer6 + 2, buffer6, sizeof(buffer6));
  6. printf("%s\n",buffer6 + 2);

运行结果:
121212121212????"
1234

几个重要库函数的实现相关推荐

  1. 实验四:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    贺邦+原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验目的: 使用库函数 ...

  2. Urllib库函数、代理、爬取案例

    Urllib库函数.代理.爬取案例 urllib库-urlopen函数用法 from urllib import request resp=request.urlopen('http://www.ba ...

  3. 1、使用库函数计算两个向量的夹角

    首先需要安装Eigen库.安装方法如下链接:https://blog.csdn.net/m0_37957160/article/details/109581546 使用到的库函数是: C ++ 中的a ...

  4. centos6.5原生系统修改ceph-mon 的ELF来让其加载低版本glibc库函数

    文章目录 Step 1:glibc-2.17 被libc.so.6库依赖,升级glibc库 Step2:升级编译器-->4.8.2可以正常编译glibc2.17 Step3:修改ELF,降低ce ...

  5. C语言字符串处理的库函数

    使用C语言字符串处理的库函数,务必包含头文件string.h,即#include <string.h>       1. 比较字符串大小函数        1) 忽略大小写---strca ...

  6. isdigit函数在C语言什么意思,C 库函数 isdigit() 使用方法及示例

    C 库函数 isdigit() 使用方法及示例 isdigit()函数检查字符是否为数字字符(0-9). isdigit()的函数原型int isdigit( int arg ); 函数isdigit ...

  7. c语言函数库哪里keyk,[精品]C语言库函数(字母G-K)-教案.doc

    [精品]C语言库函数(字母G-K)-教案 C语言库函数(字母G-K)- - ?????????????????????????????????????? (G类字母) - 1 函数名: gcvt 功 ...

  8. c程序设计语言_习题8-4_重新实现c语言的库函数fseek(FILE*fp,longoffset,intorigin)

      fseek库函数 #include <stdio.h> int fseek(FILE *stream, long int offset, int origin); 返回:成功为0,出错 ...

  9. 字符串操作系列库函数

    scanf()函数  要注意的点: #include <stdio.h>int main() {char a[100]={1,2,3,4,5,6,7,8,9,10};scanf(" ...

  10. B00009 C语言分割字符串库函数strtok

    切割字符串是常用的处理. 这里给出一个使用函数strtok切割字符串的例子. 使用C语言的库函数strtok来切割字符串的好处在于,可以指定任意字符作为分隔符来切割单词.使用该函数,切割字符串的分隔符 ...

最新文章

  1. 微信小程序使用fixed布局
  2. php execute 更新不变,php – Doctrine executeUpdate数组参数
  3. Winmail邮件服务器
  4. ubuntu16.04下配置caffe无GPU
  5. java输入流的控制_Java-Android-IO流-控制台输入输出
  6. Java中基础数据类型分类
  7. 获取android手机的屏幕分辨率 android开发
  8. alexa技能个数_如何改善Alexa技能的对话流程
  9. vep文件如何转换mp4_如何将m4v视频格式快速转换成mp4视频呢
  10. [bzoj1008] [HNOI2008]越狱
  11. 【李宏毅机器学习】Tips for Deep Learning(p14) 学习笔记
  12. python打开指定路径的excel_python使用相对定位,绝对定位,选取同级别文件下的指定文件(csv,excel)...
  13. 为什么开源应该是云原生环境的首选
  14. 推荐5大开源工具,用于开发Kubernetes项目
  15. RTMPdump 使用说明
  16. 3D Bounding Box Estimation Using Deep Learning and Geometry
  17. c++ 深度拷贝和浅度拷贝
  18. 56个民族静态字典代码创建sql语句
  19. Vba实现工作薄和工作表密码破解
  20. C语言求x个电阻并联的和的程序,C语言 计算并联电阻的阻值

热门文章

  1. java new expression,JAVA公式解析示例
  2. 树莓派跑php,在树莓派4上部署nginx+php
  3. case里面两个条件_Go语言条件语句之 switch 语句
  4. python删除txt指定内容_使用Python删除文本文件中的部分内容 | 学步园
  5. 计算机科学美国大学专业,2018美国大学计算机科学专业大排名
  6. iview select 怎么清空_如何解决iview 的select下拉框选项错位的问题,具体操作如下...
  7. android 图片跑马灯动画,ImageView 图片循环跑马灯的效果
  8. java中exception_Java中的异常 Exceptions
  9. vue打包上线部分css效果错乱,vue-cli2打包后css部分样式错乱
  10. python如何计算整数和_python 整数和浮点数