Strcpy 实现

  1. char *strcpy(char *strDest, const char *strSrc)  
  2. {  
  3.     if (strDest == NULL || strSrc == NULL)  
  4.         return NULL;  
  5.     if (strDest == strSrc)  
  6.         return strDest;  
  7.     char *tempDest = strDest;  
  8.     while((*strDest++ = *strSrc++) != '\0');  
  9.     return tempDest;  
  10. }  


strcpy的函数声明:

  1. char *strcpy(char *strDest, const char *strSrc)  

1. 为了保护原字符串不被修改,传入的原字符串用 const 修饰

2. 注意检查字符串的指针是否有效,在C中,检查字符串是否有效是判定指针是否等于NULL,而不是0 或者BOOL值

  1. if (strDest == NULL || strSrc == NULL)  
3.  注意while循环,使用后自增

4. 注意字符串拷贝的结束标志 '\0', 在while循环中,自动将原字符串的字符依次赋值给strDest,  while循环结束时已将源字符串的结束标志赋值过去,while循环内部是空操作

5. 为了适应链式表达式,该函数返回 char * 类型


Linux内核实现

strcpy是各大计算机考试和面试中几乎不可少的考点。我们平时看到最多的是下面这个版本(不考虑参数检查等操作)

  1. char * strcpy (char * dst, const char * src)  
  2. {  
  3.     char * cp = dst;  
  4.   
  5.     while( *cp++ = *src++ )  
  6.             ;               /* Copy src over dst */  
  7.     return( dst );  
  8. }  

不得不说的是,诸如*cp++ = *src++之类的代码太泛滥了,我甚至听有的人说这是老鸟才能写出的代码,⊙﹏⊙b汗!

但我们要明白的是代码最终是要机器来运行的,这样的语句翻译成机器码后,机器没有得到任何好处 -- 具体原因下文会分析... ...

glibc中的strcpy的效率要高于上面的代码,代码如下

  1. #include <stddef.h>  
  2. #include <string.h>  
  3. #include <memcopy.h>  
  4. #include <bp-checks.h>  
  5.   
  6. #undef strcpy  
  7.   
  8. /* Copy SRC to DEST.  */  
  9. char *  
  10. strcpy (dest, src)  
  11.      char *dest;  
  12.      const char *src;  
  13. {  
  14.   char c;  
  15.   char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src);  
  16.   const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1;  
  17.   size_t n;  
  18.   
  19.   do  
  20.     {  
  21.       c = *s++;  
  22.       s[off] = c;  
  23.     }  
  24.   while (c != '\0');  
  25.   
  26.   n = s - src;  
  27.   (void) CHECK_BOUNDS_HIGH (src + n);  
  28.   (void) CHECK_BOUNDS_HIGH (dest + n);  
  29.   
  30.   return dest;  
  31. }  
  32. libc_hidden_builtin_def (strcpy)  


 

  1. /* Int 5 is the "bound range" exception also raised by the "bound" 
  2.    instruction.  */  
  3. #   define BOUNDS_VIOLATED int $5  


  1. /* Verify that pointer's value >= low.  Return pointer value.  */  
  2. # define CHECK_BOUNDS_LOW(ARG)                  \  
  3.   (((__ptrvalue (ARG) < __ptrlow (ARG)) && BOUNDS_VIOLATED), \  
  4.    __ptrvalue (ARG))  
  5.   
  6. /* Verify that pointer's value < high.  Return pointer value.  */  
  7. # define CHECK_BOUNDS_HIGH(ARG)             \  
  8.   (((__ptrvalue (ARG) > __ptrhigh (ARG)) && BOUNDS_VIOLATED),    \  
  9.    __ptrvalue (ARG))  


不要被以上的代码给吓了,其实很多已经不用了。首先我们了解下bounded pointer(只是了解下)

GCC支持bounded类型指针(bounded指针用__bounded关键字标出,若默认为bounded指针,则普通指针用__unbounded标出),这种指针占用3个指针的空间,在第一个空间里存储原指针的值,第二个空间里存储下限值,第三个空间里存储上限值。__ptrvalue、__ptrlow、__ptrhigh分别返回这3个值,有了3个值以后,内存越界错误便很容易查出来了。并且要定义了__BOUNDED_POINTERS__这个宏才有作用,否则这3个宏定义都是空的。

不过,尽管bounded指针看上去似乎很有用,但是这个功能却在2003年被去掉了。因此现在所有关于bounded指针的关键字其实都是一个空的宏。

不过还是分析下这几个宏的作用吧。__ptrvalue (ARG) < __ptrlow (ARG)判断目标指针是否小于合法指针的下界,如果其结果为真,即指针越界,则执行 && 后面的BOUNDS_VIOLATED 陷入中断程序;反之,指针没有越界,则不执行BOUNDS_VIOLATED,整个表达式的值为逗号表达式后面的值,即__ptrvalue (ARG),即目标指针的值。 这么写主要是为了后面可以直接对CHECK_BOUNDS_LOW(ARG) 进行操作。

好了,鉴于此, 以上代码等价于一下代码:

  1. char *  
  2. strcpy(char *dest, const char *src)  
  3.  {  
  4.      char c;  
  5.      char *s = src;  
  6.      const ptrdiff_t off = dest - s - 1;  
  7.  /*   计算目的地址dest与源地址s的偏移-1的值。之所以要减去1,是因为后面的代码。  */
  8.      do {  
  9.           c = *s++;  
  10. /* 前面计算偏移的时候多减了一个1,就因为上一个语句s进行了自加运算 */ 
  11.           s[off] = c;  
  12.      } while (c != '\0');  
  13.      return dest;  
  14. }  

值得指出的是:此算法利用了进程平坦的内存模型,虚拟内存平坦铺开,于是任意两个指针的差就是两者之间的距离。得到地址间的相对距离off后,就不需要再用绝对地址寻址了,这样在每一次的循环中可以少一次dest++操作,而多出来的相对地址操作则完全可以用寄存器高效地完成!

 

http://blog.csdn.net/dog250/article/details/5302947


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值