几个重要库函数的实现

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

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. {  
  4.   
  5.             assert(dest!=NULL && src!=NULL);  
  6.   
  7.             char* temp=dest;  
  8.   
  9.             while((*temp++ = *src++ )!='\0')  
  10.   
  11.             {}  
  12.   
  13.             return dest;  
  14.   
  15. }  



2. strncpy

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

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

实现方式:

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

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. {  
  4.   
  5.             int i=0;  
  6.   
  7.             char* tempdest=(char*)dest;  
  8.   
  9.             char* tempsrc=(char*)src;  
  10.   
  11.             if(tempdest<tempsrc || tempdest>(tempsrc+len-1))  
  12.   
  13.             {  
  14.   
  15.                        while(i<len)  
  16.   
  17.                        {  
  18.   
  19.                                    *tempdest++ = *tempsrc++;  
  20.   
  21.                                    i++;  
  22.   
  23.                        }  
  24.   
  25.             }  
  26.   
  27.             else  
  28.   
  29.             {  
  30.   
  31.                        tempdest+=len;  
  32.   
  33.                        tempsrc+=len;  
  34.   
  35.                        i=len;  
  36.   
  37.                        while(i>0)  
  38.   
  39.                        {  
  40.   
  41.                                    *tempdest-- = *tempsrc--;  
  42.   
  43.                                    i--;  
  44.   
  45.                        }  
  46.   
  47.             }  
  48.   
  49.             return dest;  
  50.   
  51. }  


 
注意,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.   
  6. char buffer[20] = "hello";  
  7. memset(buffer, '1'sizeof(char)*20);  
  8. buffer[20] = '\0';  
  9. printf("%s\n",buffer);  
  10. 运行结果: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





本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/509957.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CDH 5.13.0 集成 Phoenix

1.下载Phoenix http://phoenix.apache.org/download.html 找到对应版本 点击parcels (cdh可安装版本) http://www.apache.org/dist/phoenix/apache-phoenix-4.14.0-cdh5.13.2/parcels/ 下载好这三个文件 -rw-r--r-- 1 root root 364830720 7月 26 16:15 APACHE_PHOENIX-…

四元数,欧拉角,旋转矩阵相互转换

#include <TransForms3d/TransForms.h>/*---------------------------------------角度弧度转换----------------------------------------*/ /*** description: 角度转为弧度* param {double} angle 角度值* return 返回对应弧度值,一般在-3.14~3.14之间*/ double Trans…

node.js事件

// 1.普通事件的使用var EventEmitterrequire(events).EventEmitter;//声明事件对象 var eventnew EventEmitter();//实例化对象 event.on(some_event,function(){//注册事件并给事件取个别名some_eventconsole.log(这是一个自定义的事件); }); setTimeout(function(){//触发事…

Phoenix 关联hbase表历史数据

Phoenix 基本使用 进入Phoenix phoenix-sqlline.py 10.248.161.18:2181:/hbase 或者进入目录下执行 cd /opt/cloudera/parcels/APACHE_PHOENIX/bin 关联Hbase原有表 创建Hbase表&#xff0c;列簇为 cf1 , cf2 create phoenix_hbase_test,cf1,cf2 插入测试数据 put phoenix…

C++面试(二)

笔试题&#xff1a; Char * const p,char const *p,const char *p分别表示什么意思? 面向对象的三个特性是什么&#xff0c;有什么意义&#xff1f; 编程实现字符串连接函数 char * strcat(char *strDest,const char *strsrc);不用C/C字符串处理库函数。 ISO七层参考模型是什…

C++面试(三)

笔试题&#xff1a; 1 写出按一个字节的位对其方式的命令 #pragma pack(n) 2 写出UpdateData(BOOL values)函数的作用 3 写字符串拷贝函数&#xff0c;不能用系统的strcpy; Char *Strcpy(char *str, const char *Srcstr); 4 对两个有序的链表head1,head2&#xff0c;把他们合并…

Phoenix 关联映射 Hbase表 获取不到数据,upsert hbase 列名为16进制字符

创建Hbase表 create phoenix_hbase_test,cf1,cf2 put phoenix_hbase_test, key1,cf1:name,zhangsan put phoenix_hbase_test, key1,cf2:age,18 put phoenix_hbase_test, key2,cf1:name,lisi put phoenix_hbase_test, key2,cf2:age,26 put phoenix_hbase_test, key3,cf1:name,c…

node.js模块和包

概念&#xff1a;模块(Module)和包(Package)是Node.js最重要的支柱。开发一个具有一定规模的程序不可能只用一个文件&#xff0c;通常需要把各个功能拆分、分装、然后组合起来。模块正式为了实现这种方式而诞生&#xff0c;在浏览器JavaScript中&#xff0c;脚本模块的拆分和组…

C++面试/技巧(四)

我们老师那个时候就唧唧歪歪的说不要写精通……这了那了的。但是经过我的个人实践才发现&#xff0c;不管你写什么&#xff0c;别人问到你简历上你提到的东西时你能回答上来。别你写着精通某个技术&#xff0c;别人问你时你却说我不是很了解只是知道一点或干脆说不会&#xff0…

Ensure that config phoenix.schema.isNamespaceMappingEnabled is consistent on client and server

Phoenix链接异常&#xff0c;报错如下 0: jdbc:phoenix:xxx:2181:/hbase> Error: ERROR 726 (43M10): Inconsistent namespace mapping properties. Ensure that config phoenix.schema.isNamespaceMappingEnabled is consistent on client and server. (state43M10,code7…

node.js包管理器和代码调式

一、如何使用包管理器 Node.js包管理器&#xff0c;即npm是Node.js官方提供的包管理工具&#xff0c;它已经成了Node.js包的标准发布平台&#xff0c;用于Node.js包的发布、传播、依赖控制。(1)获取一个包 npm [install/i] [package_name] 例如安装express包&#xff1a;…

C++面试(五)

一. 笔试题. 有考关于SIZEOFC多态.虚函数内存泄露.把一串字符串.>0放左边,小于0的放右边.EXIT() 跟 _EXIT()的区别C怎样才能实现一个类只能创建一个实例有一串字符串.将其中的小写字母转为大写字母简述下进程和线程之间的区别. 二、面试题 根据简历过一遍.里面提到的相关技…

phoenix创建索引报错“ Mutable secondary indexes must have the hbase.regionserver.wal.codec property”

phoenix 创建hbase表索引时异常&#xff0c;报错如下 Error: ERROR 1029 (42Y88): Mutable secondary indexes must have the hbase.regionserver.wal.codec property set to org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec in the hbase-sites.xml of every …

node.js全局对象和全局变量

概念&#xff1a;所有属性都可以在程序的任何地方访问&#xff0c;即全局变量。在JavaScript中&#xff0c;通常window是全局对象&#xff0c;而Node.js的全局对象是global&#xff0c;所有全局变量都是global对象的属性&#xff0c;如&#xff1a;console、process等。一、全局…

软件项目开发流程

需求分析 1.相关系统分析员向用户初步了解需求&#xff0c;然后用相关的工具软件列出要开发的系统的大功能模块&#xff0c;每个大功能模块有哪些小功能模块&#xff0c;对于有些需求比较明确相关的界面时&#xff0c;在这一步里面可以初步定义好少量的界面。[1]2.系统分析员深…

node.js util全局变量和事件驱动events

一、util全局变量 1.util.inherits(constructor,superConstructor)此方法是一个实现对象间原型继承的函数。javaScript通过原型赋值来实现继承&#xff0c;细节可参考云哥JS高级视频(原型)。案例看 util/inherits.js定义了一个基础对象Base&#xff0c;原型方法为showName&…

Phoenix 关联查询异常 , MaxServerCacheSizeExceededException phoenix.query.maxServerCacheBytes

参数配置说明&#xff1a;http://phoenix.apache.org/tuning.html Join&#xff1a;http://phoenix.apache.org/joins.html 从设计角度来讲&#xff0c;尽可能的不使用phoenix进行关联查询&#xff0c;速度比直接根据条件查询慢很多很多 测试关联&#xff0c;异常如下 Error:…

面试时,你会问面试官哪些问题?

明天又要去参加一次面试。每次面试的时候&#xff0c;面试官都会在最后给面试者一些时间&#xff0c;来问问题。这是个非常好的机会&#xff0c;能按照自己的思路&#xff0c;来了解职位、技术、企业文化、福利待遇、企业状况和前景等情况&#xff0c;以弥补前面面试过程中没有…

Phoenix 原理 以及 Phoenix在HBase中的应用

一、前言 业务使用HBase已经有一段时间了&#xff0c;期间也反馈了很多问题&#xff0c;其中反馈最多的是HBase是否支持SQL查询和二级索引&#xff0c;由于HBase在这两块上目前暂不支持&#xff0c;导致业务在使用时无法更好的利用现有的经验来查询HBase。虽然HBase本身不支持…

node.js文件操作

介绍&#xff1a;fs模块是文件操作的封装&#xff0c;它提供了文件的读取&#xff0c;写入&#xff0c;更名&#xff0c;删除&#xff0c;遍历目录&#xff0c;链接POSIX文件系统操作。与其他模块不同的是&#xff0c;fs模块中所有的操作都提供了异步和同步两个版本&#xff0c…