- char *strcpy(char *strDest, const char *strSrc)
- {
- if (strDest == NULL || strSrc == NULL)
- return NULL;
- if (strDest == strSrc)
- return strDest;
- char *tempDest = strDest;
- while((*strDest++ = *strSrc++) != '\0');
- return tempDest;
- }
strcpy的函数声明:
- char *strcpy(char *strDest, const char *strSrc)
1. 为了保护原字符串不被修改,传入的原字符串用 const 修饰
2. 注意检查字符串的指针是否有效,在C中,检查字符串是否有效是判定指针是否等于NULL,而不是0 或者BOOL值
- if (strDest == NULL || strSrc == NULL)
4. 注意字符串拷贝的结束标志 '\0', 在while循环中,自动将原字符串的字符依次赋值给strDest, while循环结束时已将源字符串的结束标志赋值过去,while循环内部是空操作
5. 为了适应链式表达式,该函数返回 char * 类型
Linux内核实现
strcpy是各大计算机考试和面试中几乎不可少的考点。我们平时看到最多的是下面这个版本(不考虑参数检查等操作)
- char * strcpy (char * dst, const char * src)
- {
- char * cp = dst;
- while( *cp++ = *src++ )
- ; /* Copy src over dst */
- return( dst );
- }
不得不说的是,诸如*cp++ = *src++之类的代码太泛滥了,我甚至听有的人说这是老鸟才能写出的代码,⊙﹏⊙b汗!
但我们要明白的是代码最终是要机器来运行的,这样的语句翻译成机器码后,机器没有得到任何好处 -- 具体原因下文会分析... ...
glibc中的strcpy的效率要高于上面的代码,代码如下
- #include <stddef.h>
- #include <string.h>
- #include <memcopy.h>
- #include <bp-checks.h>
- #undef strcpy
- /* Copy SRC to DEST. */
- char *
- strcpy (dest, src)
- char *dest;
- const char *src;
- {
- char c;
- char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src);
- const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1;
- size_t n;
- do
- {
- c = *s++;
- s[off] = c;
- }
- while (c != '\0');
- n = s - src;
- (void) CHECK_BOUNDS_HIGH (src + n);
- (void) CHECK_BOUNDS_HIGH (dest + n);
- return dest;
- }
- libc_hidden_builtin_def (strcpy)
- /* Int 5 is the "bound range" exception also raised by the "bound"
- instruction. */
- # define BOUNDS_VIOLATED int $5
- /* Verify that pointer's value >= low. Return pointer value. */
- # define CHECK_BOUNDS_LOW(ARG) \
- (((__ptrvalue (ARG) < __ptrlow (ARG)) && BOUNDS_VIOLATED), \
- __ptrvalue (ARG))
- /* Verify that pointer's value < high. Return pointer value. */
- # define CHECK_BOUNDS_HIGH(ARG) \
- (((__ptrvalue (ARG) > __ptrhigh (ARG)) && BOUNDS_VIOLATED), \
- __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) 进行操作。
好了,鉴于此, 以上代码等价于一下代码:
- char *
- strcpy(char *dest, const char *src)
- {
- char c;
- char *s = src;
- const ptrdiff_t off = dest - s - 1;
- /* 计算目的地址dest与源地址s的偏移-1的值。之所以要减去1,是因为后面的代码。 */
- do {
- c = *s++;
- /* 前面计算偏移的时候多减了一个1,就因为上一个语句s进行了自加运算 */
- s[off] = c;
- } while (c != '\0');
- return dest;
- }
值得指出的是:此算法利用了进程平坦的内存模型,虚拟内存平坦铺开,于是任意两个指针的差就是两者之间的距离。得到地址间的相对距离off后,就不需要再用绝对地址寻址了,这样在每一次的循环中可以少一次dest++操作,而多出来的相对地址操作则完全可以用寄存器高效地完成!
http://blog.csdn.net/dog250/article/details/5302947