LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1360|回复: 8

是GCC的BUG还是改进?关于局部变量内存分配位置

[复制链接]
发表于 2005-8-27 00:08:50 | 显示全部楼层 |阅读模式
在我的印象中函数内局部变量是在栈内申请内存的,请看如下代码

  1. #include <stdio.h>
  2. int main()
  3. {
  4.         int a=1;
  5.         int b=2;
  6.         int c=3;
  7. #ifdef PNTADDR
  8.         printf("%p %p %p\n", &a, &b, &c);
  9. #else
  10.         printf("%d %d %d\n",a,b,c);
  11. #endif
  12. }

复制代码


按理应是变量a占内存高位,其次是b,最次是c
用gcc-3.3 gcc-4.0 打开 PNTADDR 得到的结果验证了这一点
0xbffff9a4 0xbffff9a0 0xbffff99c
从他们的汇编码中也能看到这一点:

  1. 80483b4:       c7 45 fc 01 00 00 00    movl   $0x1,0xfffffffc(%ebp)
  2. 80483bb:       c7 45 f8 02 00 00 00    movl   $0x2,0xfffffff8(%ebp)
  3. 80483c2:       c7 45 f4 03 00 00 00    movl   $0x3,0xfffffff4(%ebp)
复制代码


但是如果关了 PNTADDR ,只打印三个整形变量的值,虽然结果一致,但用 gcc-3.3 和 gcc-4.0 生成的两个文件的汇编码却不同
gcc-3.3 还是尊照了上述原则

  1. 80483b4:       c7 45 fc 01 00 00 00    movl   $0x1,0xfffffffc(%ebp)
  2. 80483bb:       c7 45 f8 02 00 00 00    movl   $0x2,0xfffffff8(%ebp)
  3. 80483c2:       c7 45 f4 03 00 00 00    movl   $0x3,0xfffffff4(%ebp)
复制代码

但gcc-4.0 的却是这样

  1. 80483b4:       c7 45 f4 01 00 00 00    movl   $0x1,0xfffffff4(%ebp)
  2. 80483bb:       c7 45 f8 02 00 00 00    movl   $0x2,0xfffffff8(%ebp)
  3. 80483c2:       c7 45 fc 03 00 00 00    movl   $0x3,0xfffffffc(%ebp)
复制代码


比较奇怪啊.
总结:
无论如何使用局部变量(取地址或是取值),gcc-3.3 得到的汇编码的局部变量内部遵守栈分配原则,先声明的在内存高位,后的在低位.
对于gcc-4.0 ,如果下文中用到了变量的地址,则按上述原则分配内存,否则按先声明内存在低位的方式分配.

gcc-3.3 -DPNTADDR -o test3_p test.c
gcc-3.3 -o test3_v test.c
gcc-4.0 -DPNTADDR -o test4_p test.c
gcc-4.0 -o test4_v test.c

objdump -d test3_p test4_p test3_v test4_v | sed -n '/<main>/,+20p'

coder@deb3:~/progs/cc$ gcc-3.3 --version
gcc-3.3 (GCC) 3.3.6 (Debian 1:3.3.6-7)
Copyright (C) 2003 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

coder@deb3:~/progs/cc$ gcc-4.0 --version
gcc-4.0 (GCC) 4.0.1 20050701 (prerelease) (Debian 4.0.0-12)
Copyright (C) 2005 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
发表于 2005-8-27 09:16:57 | 显示全部楼层
有没有试过既输出值也输出地址呢?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-8-27 09:33:48 | 显示全部楼层
"有没有试过既输出值也输出地址呢?"
只要有取址操作,汇编码就正常。
回复 支持 反对

使用道具 举报

发表于 2005-8-27 11:10:38 | 显示全部楼层
我下了个 4.0.1 的 gcc 试了一下,得到的结论是,不是 bug, 应该是他的特性看下面的例子

  1. [rick@Fedora-Core test]$ cat test.c
  2. int main(int argc,char**argv,char**env)
  3. {
  4.         int a = 1;
  5.         int b = 2;
  6.         int c = 3;
  7.         *(int*)&b = 0;

  8.         return 0;
  9. }
  10. [rick@Fedora-Core test]$ xgcc test.c -c -o test.o
  11. [rick@Fedora-Core test]$ objdump -d test.o

  12. test.o:     file format elf32-i386

  13. Disassembly of section .text:

  14. 00000000 <main>:
  15.    0:   55                      push   %ebp
  16.    1:   89 e5                   mov    %esp,%ebp
  17.    3:   83 ec 18                sub    $0x18,%esp
  18.    6:   83 e4 f0                and    $0xfffffff0,%esp
  19.    9:   b8 00 00 00 00          mov    $0x0,%eax
  20.    e:   83 c0 0f                add    $0xf,%eax
  21.   11:   83 c0 0f                add    $0xf,%eax
  22.   14:   c1 e8 04                shr    $0x4,%eax
  23.   17:   c1 e0 04                shl    $0x4,%eax
  24.   1a:   29 c4                   sub    %eax,%esp
  25.   1c:   c7 45 f8 01 00 00 00    movl   $0x1,0xfffffff8(%ebp)
  26.   23:   c7 45 f4 02 00 00 00    movl   $0x2,0xfffffff4(%ebp)
  27.   2a:   c7 45 fc 03 00 00 00    movl   $0x3,0xfffffffc(%ebp)
  28.   31:   c7 45 f4 00 00 00 00    movl   $0x0,0xfffffff4(%ebp)
  29.   38:   b8 00 00 00 00          mov    $0x0,%eax
  30.   3d:   c9                      leave
  31.   3e:   c3                      ret
复制代码


可以看到,如果其他的变量没有取地址,而只有 b 取了地址,b 就被放在最下面,而 a 和 c 还按照那种"不寻常的方式"存放.
就是说新的gcc对是否有取地址操作的变量在分配其地址时是区别对待的,至于为什么要这样,还不清楚
回复 支持 反对

使用道具 举报

发表于 2005-8-27 11:15:02 | 显示全部楼层
另外,在你的printf的例子中有没有发现另一个特性: 函数调用不是压栈操作,而是直接像栈里面写入参数,在这些参数所需的空间,在 main 函数刚开始的时候就已经分配好了,这有一个好处,就是免去了每次函数调用时都需要考虑 16 字节对齐的问题,只在main函数开始对齐一下,以后的函数调用的参数都从一个固定的地方写入栈(至于是否有其他方面的考虑,就不清楚了)
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-8-27 17:20:39 | 显示全部楼层
Post by rickxbx
另外,在你的printf的例子中有没有发现另一个特性: 函数调用不是压栈操作,而是直接像栈里面写入参数,在这些参数所需的空间,在 main 函数刚开始的时候就已经分配好了,这有一个好处,就是免去了每次函数调用时都需要考虑 16 字节对齐的问题,只在main函数开始对齐一下,以后的函数调用的参数都从一个固定的地方写入栈(至于是否有其他方面的考虑,就不清楚了)

rickxbx兄,我对汇编语言是一知半解, 你的这段话我还不能完全理解. 我想这些问题可能是GCC所采取的一些优化措施.
但不知这样搞到底有什么好处呢?
另外, 想请教一下, 有没有好点的LINUX下汇编资料可以推荐一下?
回复 支持 反对

使用道具 举报

发表于 2005-8-27 20:48:32 | 显示全部楼层
Post by 弥敦路九号

但不知这样搞到底有什么好处呢?

我也不知道这样是不是优化措施,
但是起码这样编译器实现起来更简洁.
Post by 弥敦路九号

另外, 想请教一下, 有没有好点的LINUX下汇编资料可以推荐一下?

http://linuxassembly.org/
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-8-27 23:07:29 | 显示全部楼层
谢谢了,资源很丰富,正在研究
回复 支持 反对

使用道具 举报

发表于 2005-8-28 09:33:41 | 显示全部楼层
Post by rickxbx
另外,在你的printf的例子中有没有发现另一个特性: 函数调用不是压栈操作,而是直接像栈里面写入参数,在这些参数所需的空间,在 main 函数刚开始的时候就已经分配好了,这有一个好处,就是免去了每次函数调用时都需要考虑 16 字节对齐的问题,只在main函数开始对齐一下,以后的函数调用的参数都从一个固定的地方写入栈(至于是否有其他方面的考虑,就不清楚了)


更正一下,这样的情况在 gcc4.0.1才会出现,4.0不会出现
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表