几个重要库函数的实现

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

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-…

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…

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;脚本模块的拆分和组…

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…

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:…

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…

phoenix 使用详细介绍 创建二级索引

phoenix 关联hbase 基本使用参考&#xff1a;https://blog.csdn.net/zhangshenghang/article/details/97491597 Phoenix 创建二级索引 hbase中有表test_article ,在表空间 test_ns ,列簇 fn 下有字段 url , text , uid ,name 连接phoenixphoenix-sqlline.py hostname:2181:/hb…

node.js详解Http服务器

概念&#xff1a;Node.js提供了http模块。其中封装了一个高效的HTTP服务器和一个建议的HTTP客户端。http.server是一个基于事件的HTTP服务器。内部有C实现。接口由JavaScript封装。http.request则是一个HTTP客户端工具。用户向服务器发送请求。一、HTTP服务器 http.Server实现的…

node.js http客户端

一、http模块提供了两个函数http.request和http.get&#xff0c;功能是作为客户端向HTTP服务器发起请求。 Ext.Ajax.request({},function(response))1.http.request(options,callback)发起HTTP请求&#xff0c;接受两个参数&#xff0c;option是一个类似关联数组的对象&#xf…

CDH kerberos 认证,安全认证

环境centos 7.4 安装KDC服务 yum -y install krb5-server krb5-libs krb5-auth-dialog krb5-workstation 修改配置文件 vi /etc/krb5.conf 默认如下 修改为 # Configuration snippets may be placed in this directory as well includedir /etc/krb5.conf.d/[logging]default…

虚拟继承和虚表

普通继承和虚拟继承类的大小变化&#xff1a; 普通继承&#xff1a; 虚拟继承&#xff1a; 类A和类B大小为&#xff1a; 由此可见&#xff1a;1、类中静态成员不会影…

Kerberos 下运行spark 报错 Requested user hdfs is not whitelisted and has id 995,which is below the minimu

异常如下 main : run as user is hdfs main : requested yarn user is hdfs Requested user hdfs is not whitelisted and has id 995,which is below the minimum allowed 1000 问题原因&#xff1a;是由于Yarn限制了用户id小于1000的用户提交作业&#xff1b; 解决方法&a…

kerberos 下运行spark 报错 Requested user hdfs is banned

启动运行报错 main : run as user is hdfs main : requested yarn user is hdfs Requested user hdfs is bannedFailing this attempt. Failing the application.ApplicationMaster host: N/AApplicationMaster RPC port: -1queue: root.defaultstart time: 1565170753121fina…

node.js路由控制

一、工作原理 当通过浏览器访问app.js建立的服务器时&#xff0c;会看到一个简单的页面&#xff0c;实际上它已经完成了许多透明的工作&#xff0c;当访问http://localhost:3000&#xff0c;浏览器会向服务器发送请求&#xff0c;包括请求的方法、路径、HTTP协议版本和请求头信…

node.js模块引擎

一、什么是模版引擎 模版引擎是一个从页面模版根据一定的规则生成HTML的工具&#xff0c;PHP首发&#xff0c;随后出现了ASP、JSP都沿用这个模式&#xff0c;即建立一个HTML页面模版&#xff0c;插入可执行的代码。运行时动态生成HTML。缺点&#xff1a; 页面功能逻辑与页面布局…

HBase ACL管理 Hbase 权限管理

场景&#xff1a;hadoop集群已经进行kerberos认证 启动Hbase相关配置Hbase权限分为以下五种&#xff1a;Read(R) : 可以读取给定范围内数据的权限 Write(W) : 可以在给定范围内写数据 Executor(X) : 可以在指定表执行Endpoints类型的协处理 Create(C) : 可以在给定范围内创建和…

MySql索引的原理

数据库索引&#xff0c;是数据库管理系统中一个排序的数据结构&#xff0c;以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B树。 在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&#xff08…

Hadoop Kerberos 认证下 Sentry 安装 + Sentry 权限设置使用

目录 一、安装Sentry &#xff11;.&#xff2d;ariaDB中创建sentry数据库 2.CDH中添加sentry 服务 3.hive配置 启动Sentry 4.Impala配置 启动Sentry 5.Hue配置 启动Sentry 6.Hdfs配置 启动Sentry 7.重启服务&#xff0c;使配置生效 二、Sentry权限测试 1.创建hive超…