linux gcc对于函数栈中变量内存分配问题

时间:2020-12-04 02:23:36
今天测试函数栈的时候发现一个很奇怪的问题:在函数中定义的变量,如果后面有对该变量的取地址操作,则此变量从高地址向低地址分配空间,否则,则变量在栈中从低地址向高地址分配空间。
        有没有谁注意到这个问题,请帮我分析下?



5 个解决方案

#1


gcc版本 4.1.2

#2


你怎么看的地址分配?  反汇编看看

#3


分析了下好像是这样的:如果某变量有取地址操作,则将此变量尽量放在栈顶(相对于没有取地址操作的变量)
比如变量:int a,b,c,d,e,f;
如果 a,c,f 有取地址操作,则栈分配应该是这样的:
b
d
e
a
c
f
上图栈顶的方向是向下的。
这是我的C代码

#include <stdio.h>

void test(int x,int y)
{
        printf("x:%d\n",x);
        printf("y:%d\n",y);
}
int main()
{

        int a = 0;
        int b = 0;
        int c = 0;
        char x = 0;
        char y = 0;
        char z = 0;
        test(a,b);

        printf("a:%p\n",&a);
/*      printf("b:%p\n",&b);
        printf("c:%p\n",&c);
        printf("x:%p\n",&z);
        printf("y:%p\n",&y);
        printf("z:%p\n",&z);
        */
        return 0;
}




objdump的main函数如下:

int main()
{
 804846a:       55                      push   %ebp
 804846b:       89 e5                   mov    %esp,%ebp
 804846d:       83 e4 f0                and    $0xfffffff0,%esp
 8048470:       83 ec 20                sub    $0x20,%esp

        int a = 0;
 8048473:       c7 44 24 10 00 00 00    movl   $0x0,0x10(%esp)
 804847a:       00 
        int b = 0;
 804847b:       c7 44 24 1c 00 00 00    movl   $0x0,0x1c(%esp)
 8048482:       00 
        int c = 0;
 8048483:       c7 44 24 18 00 00 00    movl   $0x0,0x18(%esp)
 804848a:       00 
        char x = 0;
 804848b:       c6 44 24 17 00          movb   $0x0,0x17(%esp)
        char y = 0;
 8048490:       c6 44 24 16 00          movb   $0x0,0x16(%esp)
        char z = 0;
 8048495:       c6 44 24 15 00          movb   $0x0,0x15(%esp)
        test(a,b);
 804849a:       8b 44 24 10             mov    0x10(%esp),%eax
 804849e:       8b 54 24 1c             mov    0x1c(%esp),%edx
 80484a2:       89 54 24 04             mov    %edx,0x4(%esp)
 80484a6:       89 04 24                mov    %eax,(%esp)
 80484a9:       e8 8e ff ff ff          call   804843c <test>

        printf("a:%p\n",&a);
 80484ae:       8d 44 24 10             lea    0x10(%esp),%eax
 80484b2:       89 44 24 04             mov    %eax,0x4(%esp)
 80484b6:       c7 04 24 6c 85 04 08    movl   $0x804856c,(%esp)
 80484bd:       e8 4e fe ff ff          call   8048310 <printf@plt>
        printf("c:%p\n",&c);
        printf("x:%p\n",&z);
        printf("y:%p\n",&y);
        printf("z:%p\n",&z);
        */
        return 0;
 80484c2:       b8 00 00 00 00          mov    $0x0,%eax
}



#4


这个好像没什么规律啊。
楼上的嗲吗我试验了两个系统,结果如下,没有对取地址的变量特殊处理吧。
只是看起来,不同版本的gcc对局部变量的内存分配顺序策略有所不同。
FreeBSD8.1下gcc 4.2.1

08048450 <main>:
int main()
{
 8048450:       8d 4c 24 04             lea    0x4(%esp),%ecx
 8048454:       83 e4 f0                and    $0xfffffff0,%esp
 8048457:       ff 71 fc                pushl  0xfffffffc(%ecx)
 804845a:       55                      push   %ebp
 804845b:       89 e5                   mov    %esp,%ebp
 804845d:       51                      push   %ecx
 804845e:       83 ec 24                sub    $0x24,%esp
 
        int a = 0;
 8048461:       c7 45 ec 00 00 00 00    movl   $0x0,0xffffffec(%ebp)
        int b = 0;
 8048468:       c7 45 f0 00 00 00 00    movl   $0x0,0xfffffff0(%ebp)
        int c = 0;
 804846f:       c7 45 f4 00 00 00 00    movl   $0x0,0xfffffff4(%ebp)
        char x = 0;
 8048476:       c6 45 f9 00             movb   $0x0,0xfffffff9(%ebp)
        char y = 0;
 804847a:       c6 45 fa 00             movb   $0x0,0xfffffffa(%ebp)
        char z = 0;
 804847e:       c6 45 fb 00             movb   $0x0,0xfffffffb(%ebp)
        test(a,b);
 8048482:       8b 55 ec                mov    0xffffffec(%ebp),%edx
 8048485:       8b 45 f0                mov    0xfffffff0(%ebp),%eax
 8048488:       89 44 24 04             mov    %eax,0x4(%esp)
 804848c:       89 14 24                mov    %edx,(%esp)
 804848f:       e8 8c ff ff ff          call   8048420 <test>
 
        printf("a:%p\n",&a);
 8048494:       8d 45 ec                lea    0xffffffec(%ebp),%eax
 8048497:       89 44 24 04             mov    %eax,0x4(%esp)
 804849b:       c7 04 24 5d 85 04 08    movl   $0x804855d,(%esp)
 80484a2:       e8 25 fe ff ff          call   80482cc <_init+0x34>
                /*
        printf("b:%p\n",&b);
        printf("c:%p\n",&c);
        printf("x:%p\n",&z);
        printf("y:%p\n",&y);
        printf("z:%p\n",&z);
        */
        return 0;
 80484a7:       b8 00 00 00 00          mov    $0x0,%eax
}

Linux 2.6.32-131.0.15.el6.x86_64 x86_64, gcc version 4.4.5 20110214 (Red Hat 4.4.5-6) (GCC) 

0000000000400502 <main>:
int main()
{
  400502:       55                      push   %rbp
  400503:       48 89 e5                mov    %rsp,%rbp
  400506:       48 83 ec 10             sub    $0x10,%rsp
 
        int a = 0;
  40050a:       c7 45 f0 00 00 00 00    movl   $0x0,-0x10(%rbp)
        int b = 0;
  400511:       c7 45 f4 00 00 00 00    movl   $0x0,-0xc(%rbp)
        int c = 0;
  400518:       c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%rbp)
        char x = 0;
  40051f:       c6 45 fd 00             movb   $0x0,-0x3(%rbp)
        char y = 0;
  400523:       c6 45 fe 00             movb   $0x0,-0x2(%rbp)
        char z = 0;
  400527:       c6 45 ff 00             movb   $0x0,-0x1(%rbp)
        test(a,b);
  40052b:       8b 45 f0                mov    -0x10(%rbp),%eax
  40052e:       8b 55 f4                mov    -0xc(%rbp),%edx
  400531:       89 d6                   mov    %edx,%esi
  400533:       89 c7                   mov    %eax,%edi
  400535:       e8 8a ff ff ff          callq  4004c4 <test>
 
        printf("a:%p\n",&a);
  40053a:       b8 64 06 40 00          mov    $0x400664,%eax
  40053f:       48 8d 55 f0             lea    -0x10(%rbp),%rdx
  400543:       48 89 d6                mov    %rdx,%rsi
  400546:       48 89 c7                mov    %rax,%rdi
  400549:       b8 00 00 00 00          mov    $0x0,%eax
  40054e:       e8 65 fe ff ff          callq  4003b8 <printf@plt>
        printf("c:%p\n",&c);
        printf("x:%p\n",&z);
        printf("y:%p\n",&y);
        printf("z:%p\n",&z);
        */
        return 0;
  400553:       b8 00 00 00 00          mov    $0x0,%eax
}

#5


引用 4 楼 mymtom 的回复:
这个好像没什么规律啊。
楼上的嗲吗我试验了两个系统,结果如下,没有对取地址的变量特殊处理吧。
只是看起来,不同版本的gcc对局部变量的内存分配顺序策略有所不同。

感谢mymtom的帮助!
你的第二个系统好像就对取地址的变量有了特殊操作,局部变量分配顺序是从栈顶到栈底,不管怎么说b应该是在变量队列的中间,现在却在栈顶。
这个是不是有的GCC版本会有这样的操作,但是其中的原因却有些觉历不明啊,我胡乱猜测下原因,不知道对不对,这样做可能是想把有取地址操作的变量放在一块,系统在做取地址操作的时候,一次也把附近的变量也做取地址操作,避免重复的劳作。


 int a = 0;
  40050a:       c7 45 f0 00 00 00 00    movl   $0x0,-0x10(%rbp)
        int b = 0;
  400511:       c7 45 f4 00 00 00 00    movl   $0x0,-0xc(%rbp)
        int c = 0;
  400518:       c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%rbp)
        char x = 0;

#1


gcc版本 4.1.2

#2


你怎么看的地址分配?  反汇编看看

#3


分析了下好像是这样的:如果某变量有取地址操作,则将此变量尽量放在栈顶(相对于没有取地址操作的变量)
比如变量:int a,b,c,d,e,f;
如果 a,c,f 有取地址操作,则栈分配应该是这样的:
b
d
e
a
c
f
上图栈顶的方向是向下的。
这是我的C代码

#include <stdio.h>

void test(int x,int y)
{
        printf("x:%d\n",x);
        printf("y:%d\n",y);
}
int main()
{

        int a = 0;
        int b = 0;
        int c = 0;
        char x = 0;
        char y = 0;
        char z = 0;
        test(a,b);

        printf("a:%p\n",&a);
/*      printf("b:%p\n",&b);
        printf("c:%p\n",&c);
        printf("x:%p\n",&z);
        printf("y:%p\n",&y);
        printf("z:%p\n",&z);
        */
        return 0;
}




objdump的main函数如下:

int main()
{
 804846a:       55                      push   %ebp
 804846b:       89 e5                   mov    %esp,%ebp
 804846d:       83 e4 f0                and    $0xfffffff0,%esp
 8048470:       83 ec 20                sub    $0x20,%esp

        int a = 0;
 8048473:       c7 44 24 10 00 00 00    movl   $0x0,0x10(%esp)
 804847a:       00 
        int b = 0;
 804847b:       c7 44 24 1c 00 00 00    movl   $0x0,0x1c(%esp)
 8048482:       00 
        int c = 0;
 8048483:       c7 44 24 18 00 00 00    movl   $0x0,0x18(%esp)
 804848a:       00 
        char x = 0;
 804848b:       c6 44 24 17 00          movb   $0x0,0x17(%esp)
        char y = 0;
 8048490:       c6 44 24 16 00          movb   $0x0,0x16(%esp)
        char z = 0;
 8048495:       c6 44 24 15 00          movb   $0x0,0x15(%esp)
        test(a,b);
 804849a:       8b 44 24 10             mov    0x10(%esp),%eax
 804849e:       8b 54 24 1c             mov    0x1c(%esp),%edx
 80484a2:       89 54 24 04             mov    %edx,0x4(%esp)
 80484a6:       89 04 24                mov    %eax,(%esp)
 80484a9:       e8 8e ff ff ff          call   804843c <test>

        printf("a:%p\n",&a);
 80484ae:       8d 44 24 10             lea    0x10(%esp),%eax
 80484b2:       89 44 24 04             mov    %eax,0x4(%esp)
 80484b6:       c7 04 24 6c 85 04 08    movl   $0x804856c,(%esp)
 80484bd:       e8 4e fe ff ff          call   8048310 <printf@plt>
        printf("c:%p\n",&c);
        printf("x:%p\n",&z);
        printf("y:%p\n",&y);
        printf("z:%p\n",&z);
        */
        return 0;
 80484c2:       b8 00 00 00 00          mov    $0x0,%eax
}



#4


这个好像没什么规律啊。
楼上的嗲吗我试验了两个系统,结果如下,没有对取地址的变量特殊处理吧。
只是看起来,不同版本的gcc对局部变量的内存分配顺序策略有所不同。
FreeBSD8.1下gcc 4.2.1

08048450 <main>:
int main()
{
 8048450:       8d 4c 24 04             lea    0x4(%esp),%ecx
 8048454:       83 e4 f0                and    $0xfffffff0,%esp
 8048457:       ff 71 fc                pushl  0xfffffffc(%ecx)
 804845a:       55                      push   %ebp
 804845b:       89 e5                   mov    %esp,%ebp
 804845d:       51                      push   %ecx
 804845e:       83 ec 24                sub    $0x24,%esp
 
        int a = 0;
 8048461:       c7 45 ec 00 00 00 00    movl   $0x0,0xffffffec(%ebp)
        int b = 0;
 8048468:       c7 45 f0 00 00 00 00    movl   $0x0,0xfffffff0(%ebp)
        int c = 0;
 804846f:       c7 45 f4 00 00 00 00    movl   $0x0,0xfffffff4(%ebp)
        char x = 0;
 8048476:       c6 45 f9 00             movb   $0x0,0xfffffff9(%ebp)
        char y = 0;
 804847a:       c6 45 fa 00             movb   $0x0,0xfffffffa(%ebp)
        char z = 0;
 804847e:       c6 45 fb 00             movb   $0x0,0xfffffffb(%ebp)
        test(a,b);
 8048482:       8b 55 ec                mov    0xffffffec(%ebp),%edx
 8048485:       8b 45 f0                mov    0xfffffff0(%ebp),%eax
 8048488:       89 44 24 04             mov    %eax,0x4(%esp)
 804848c:       89 14 24                mov    %edx,(%esp)
 804848f:       e8 8c ff ff ff          call   8048420 <test>
 
        printf("a:%p\n",&a);
 8048494:       8d 45 ec                lea    0xffffffec(%ebp),%eax
 8048497:       89 44 24 04             mov    %eax,0x4(%esp)
 804849b:       c7 04 24 5d 85 04 08    movl   $0x804855d,(%esp)
 80484a2:       e8 25 fe ff ff          call   80482cc <_init+0x34>
                /*
        printf("b:%p\n",&b);
        printf("c:%p\n",&c);
        printf("x:%p\n",&z);
        printf("y:%p\n",&y);
        printf("z:%p\n",&z);
        */
        return 0;
 80484a7:       b8 00 00 00 00          mov    $0x0,%eax
}

Linux 2.6.32-131.0.15.el6.x86_64 x86_64, gcc version 4.4.5 20110214 (Red Hat 4.4.5-6) (GCC) 

0000000000400502 <main>:
int main()
{
  400502:       55                      push   %rbp
  400503:       48 89 e5                mov    %rsp,%rbp
  400506:       48 83 ec 10             sub    $0x10,%rsp
 
        int a = 0;
  40050a:       c7 45 f0 00 00 00 00    movl   $0x0,-0x10(%rbp)
        int b = 0;
  400511:       c7 45 f4 00 00 00 00    movl   $0x0,-0xc(%rbp)
        int c = 0;
  400518:       c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%rbp)
        char x = 0;
  40051f:       c6 45 fd 00             movb   $0x0,-0x3(%rbp)
        char y = 0;
  400523:       c6 45 fe 00             movb   $0x0,-0x2(%rbp)
        char z = 0;
  400527:       c6 45 ff 00             movb   $0x0,-0x1(%rbp)
        test(a,b);
  40052b:       8b 45 f0                mov    -0x10(%rbp),%eax
  40052e:       8b 55 f4                mov    -0xc(%rbp),%edx
  400531:       89 d6                   mov    %edx,%esi
  400533:       89 c7                   mov    %eax,%edi
  400535:       e8 8a ff ff ff          callq  4004c4 <test>
 
        printf("a:%p\n",&a);
  40053a:       b8 64 06 40 00          mov    $0x400664,%eax
  40053f:       48 8d 55 f0             lea    -0x10(%rbp),%rdx
  400543:       48 89 d6                mov    %rdx,%rsi
  400546:       48 89 c7                mov    %rax,%rdi
  400549:       b8 00 00 00 00          mov    $0x0,%eax
  40054e:       e8 65 fe ff ff          callq  4003b8 <printf@plt>
        printf("c:%p\n",&c);
        printf("x:%p\n",&z);
        printf("y:%p\n",&y);
        printf("z:%p\n",&z);
        */
        return 0;
  400553:       b8 00 00 00 00          mov    $0x0,%eax
}

#5


引用 4 楼 mymtom 的回复:
这个好像没什么规律啊。
楼上的嗲吗我试验了两个系统,结果如下,没有对取地址的变量特殊处理吧。
只是看起来,不同版本的gcc对局部变量的内存分配顺序策略有所不同。

感谢mymtom的帮助!
你的第二个系统好像就对取地址的变量有了特殊操作,局部变量分配顺序是从栈顶到栈底,不管怎么说b应该是在变量队列的中间,现在却在栈顶。
这个是不是有的GCC版本会有这样的操作,但是其中的原因却有些觉历不明啊,我胡乱猜测下原因,不知道对不对,这样做可能是想把有取地址操作的变量放在一块,系统在做取地址操作的时候,一次也把附近的变量也做取地址操作,避免重复的劳作。


 int a = 0;
  40050a:       c7 45 f0 00 00 00 00    movl   $0x0,-0x10(%rbp)
        int b = 0;
  400511:       c7 45 f4 00 00 00 00    movl   $0x0,-0xc(%rbp)
        int c = 0;
  400518:       c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%rbp)
        char x = 0;