免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 4058 | 回复: 11
打印 上一主题 下一主题

[C] 让人看不懂的strcpy.c [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-11-21 11:11 |只看该作者 |倒序浏览
这是glibc6里面,strcpy.c的代码:

  1. 01   char *
  2. 02  strcpy (dest, src)
  3. 03       char *dest;
  4. 04       const char *src;
  5. 05  {
  6. 06    reg_char c;
  7. 07    char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src);
  8. 08    const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1;
  9. 09    size_t n;
  10. 10
  11. 11     do
  12. 12      {
  13. 13        c = *s++;
  14. 14        s[off] = c;
  15. 15      }
  16. 16    while (c != '\0');
  17. 17
  18. 18    n = s - src;
  19. 19    (void) CHECK_BOUNDS_HIGH (src + n);
  20. 20   (void) CHECK_BOUNDS_HIGH (dest + n);
  21. 21
  22. 22   return dest;
  23. 23  }
  24. 24 libc_hidden_builtin_def (strcpy)
复制代码


在bp-checks.h中定义有这2个宏:
/* 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))

cdefs.h文件中定义了这些宏
/* Support for bounded pointers.  */
#ifndef __BOUNDED_POINTERS__
# define __bounded      /* nothing */
# define __unbounded    /* nothing */
# define __ptrvalue     /* nothing */
#endif

我查看了一下bounded指针的涵义,貌似gcc不支持?所以优化过后的代码应该是:

  1. 01   char *
  2. 02  strcpy (char *dest, char *src)
  3. 03  {
  4. 04    register char c;
  5. 05    const ptrdiff_t off = dest - src - 1;
  6. 06
  7. 07    do
  8. 08      {
  9. 09        c = *src++;
  10. 10        src[off] = c;
  11. 11       }
  12. 12    while (c != '\0');
  13. 13
  14. 14   return dest;
  15. 15  }
复制代码

我感到疑惑的是第05行,不是在一个数组里面的指针,减出来是什么?
很自然的
11     do
12      {
13        c = *src++;
14        src[off] = c;
15      }
16    while (c != '\0');
我也就看不懂了。

请大家指点迷津,在下有礼了...


更新一下,看来就是通过用src[off]代替dest作拷贝(觉得这里很取巧啊...让我想到了变长结构体)。如果c能成为一个寄存器变量,那么效率会比 *dest++ = *src++ 高吧。

看来早上学习效率真的不如下午啊...

[ 本帖最后由 imonyse 于 2007-11-21 13:20 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2007-11-21 12:01 |只看该作者
我猜是计算出 dest 和 src 在内存中的差值, 然后那个while 循环,是跟src这个内存地址 和off 这个差值找到dest。。

论坛徽章:
0
3 [报告]
发表于 2007-11-21 12:54 |只看该作者
这样做和直接
  *dest++ = *src++
有效率上的区别么???

论坛徽章:
0
4 [报告]
发表于 2007-11-21 13:15 |只看该作者
看来就是通过用src[off]代替dest作拷贝。如果c能成为一个寄存器变量,那么效率会比 *dest++ = *src++ 高吧。

论坛徽章:
0
5 [报告]
发表于 2007-11-21 13:22 |只看该作者
原帖由 imonyse 于 2007-11-21 13:15 发表
看来就是通过用src[off]代替dest作拷贝。如果c能成为一个寄存器变量,那么效率会比 *dest++ = *src++ 高吧。

我是问用src[off]来代替dest++操作有什么好处么,  至于c寄存器变量,也可以用在dest++上来

论坛徽章:
0
6 [报告]
发表于 2007-11-21 14:20 |只看该作者
就是效率吧
src[off]版的汇编片段:

  1. my_strcpy:
  2.         pushl   %ebp
  3.         movl    %esp, %ebp
  4.         subl    $16, %esp
  5.         movl    8(%ebp), %edx
  6.         movl    12(%ebp), %eax
  7.         movl    %edx, %ecx
  8.         subl    %eax, %ecx
  9.         movl    %ecx, %eax
  10.         subl    $1, %eax
  11.         movl    %eax, -4(%ebp)
  12. .L2:
  13.         movl    12(%ebp), %eax
  14.         movzbl  (%eax), %edx
  15.         addl    $1, 12(%ebp)
  16.         movl    -4(%ebp), %eax
  17.         addl    12(%ebp), %eax
  18.         movb    %dl, (%eax)
  19.         testb   %dl, %dl
  20.         jne     .L2
  21.         movl    8(%ebp), %eax
  22.         leave
  23.         ret
复制代码

*dest++=*src++版的汇编:

  1. my_strcpy:
  2.         pushl   %ebp
  3.         movl    %esp, %ebp
  4.         jmp     .L2
  5. .L3:
  6.         movl    12(%ebp), %eax
  7.         movzbl  (%eax), %edx
  8.         movl    8(%ebp), %eax
  9.         movb    %dl, (%eax)
  10.         addl    $1, 8(%ebp)
  11.         addl    $1, 12(%ebp)
  12. .L2:
  13.         cmpl    $0, 12(%ebp)
  14.         jne     .L3
  15.         movl    8(%ebp), %eax
  16.         popl    %ebp
  17.         ret
复制代码


编译器是gcc4.1
不是很懂汇编(以后再学...)

[ 本帖最后由 imonyse 于 2007-11-21 14:21 编辑 ]

论坛徽章:
0
7 [报告]
发表于 2007-11-21 15:10 |只看该作者
学习中

论坛徽章:
0
8 [报告]
发表于 2007-11-21 15:14 |只看该作者
gnu的代码确实读起来比较费劲,还是喜欢FreeBSD的代码,简洁漂亮:


  1. sys/libkern/strcpy.c

  2. 35 char *
  3. 36 strcpy(char * __restrict to, const char * __restrict from)
  4. 37 {
  5. 38         char *save = to;
  6. 39
  7. 40         for (; (*to = *from) != 0; ++from, ++to);
  8. 41         return(save);
  9. 42 }
  10. 43
复制代码

论坛徽章:
0
9 [报告]
发表于 2007-11-21 22:58 |只看该作者
对比下面两个循环

while( (src[off] = *src++) != 0 )  ;
1. 从src表示的地址处读取数据
2. 将数据存放到src+off表示的地址处
3. src++
4. 判断取出的数是否为0,如果是0,则结束循环,否则继续执行1

while( (*dst++ = *src++) != 0 )   ;
1. 从src表示的地址处读取数据
2. 将数据存放到dst表示的地址处
3. src++
4. dst++
5. 判断取出的数是否为0,如果是0,则结束循环,否则继续执行1

x86 CPU可以通过
通用寄存器 + 2/4/8/16 * 通用寄存器 + 偏移
的方式寻址, 它和通过通用寄存器来寻址所耗费的周期是一样的,那么第一段代码比第二段代码在每次循环中少做一个++操作,从而获得更高的效率。

论坛徽章:
0
10 [报告]
发表于 2007-11-22 02:24 |只看该作者
效率的话 ,感觉汇编最高~
REP MOVSB
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP