几个重要库函数的实现

来源:互联网 发布:java 连接sqlserver 编辑:程序博客网 时间:2024/06/02 05:06

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

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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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





0 0
原创粉丝点击