Chinaunix首页 | 论坛 | 博客
  • 博客访问: 385342
  • 博文数量: 39
  • 博客积分: 1771
  • 博客等级: 上尉
  • 技术积分: 1231
  • 用 户 组: 普通用户
  • 注册时间: 2005-05-01 14:21
文章分类

全部博文(39)

文章存档

2013年(1)

2012年(4)

2011年(16)

2010年(6)

2009年(12)

分类: LINUX

2009-12-25 10:34:25

今天跟同事争论sizeof到底是编译时确定的值还是运行时确定的值,那么我们让我们用汇编来验证一下gcc的实现机制。

情形一:单个变量
验证程序:

#include <stdio.h>

int
main()
{
       
int a;
        int b = sizeof(a);
        printf("%d\n", b);
}

gcc -O3 -S的结果:

1 .file "t.c"
     2 .section .rodata.str1.1,"aMS",@progbits,1
     3 .LC0:
     4 .string "%d\n"
     5 .text
     6 .p2align 4,,15
     7 .globl main
     8 .type main, @function
     9 main:
    10 leal 4(%esp), %ecx
    11 andl $-16, %esp
    12 pushl -4(%ecx)
    13 pushl %ebp
    14 movl %esp, %ebp
    15 pushl %ecx
    16 subl $20, %esp
    17 movl $4, 4(%esp)
    18 movl $.LC0, (%esp)
    19 call printf
    20 addl $20, %esp
    21 popl %ecx
    22 popl %ebp
    23 leal -4(%ecx), %esp
    24 ret
    25 .size main, .-main
    26 .ident "GCC: (Debian 4.3.2-1.1) 4.3.2"
    27 .section .note.GNU-stack,"",@progbits

注意第17行,编译时就把把变量在内存的大小4字节压栈了。

情形二:数组
验证程序:

#include <stdio.h>

int
main()
{
       
char a[10];
        int b = sizeof(a);
        printf("%d\n", b);
}

gcc -O3 -S的结果:

     1 .file "t.c"
     2 .section .rodata.str1.1,"aMS",@progbits,1
     3 .LC0:
     4 .string "%d\n"
     5 .text
     6 .p2align 4,,15
     7 .globl main
     8 .type main, @function
     9 main:
    10 leal 4(%esp), %ecx
    11 andl $-16, %esp
    12 pushl -4(%ecx)
    13 pushl %ebp
    14 movl %esp, %ebp
    15 pushl %ecx
    16 subl $20, %esp
    17 movl $10, 4(%esp)
    18 movl $.LC0, (%esp)
    19 call printf
    20 addl $20, %esp
    21 popl %ecx
    22 popl %ebp
    23 leal -4(%ecx), %esp
    24 ret
    25 .size main, .-main
    26 .ident "GCC: (Debian 4.3.2-1.1) 4.3.2"
    27 .section .note.GNU-stack,"",@progbits

注意第17行,也是编译时就把数组的大小10压栈了。

情形三:可变长度数组
现在gcc支持可变长度数组了,情况要复杂一些了。测试程序:

#include <stdio.h>

int
main(int argc, char **argv)
{
        int a[argc];
        int b = sizeof(a);
        printf("%d\n", b);
}

gcc -O3 -S的结果:

     1 .file "t.c"
     2 .section .rodata.str1.1,"aMS",@progbits,1
     3 .LC0:
     4 .string "%d\n"
     5 .text
     6 .p2align 4,,15
     7 .globl main
     8 .type main, @function
     9 main:
    10 leal 4(%esp), %ecx        #把第一个参数的地址放入ecx。main的第一个参数即argc的值,也就是我们数组的长度
    11 andl $-16, %esp
    12 pushl -4(%ecx)            #返回地址压栈
    13 pushl %ebp                #建立stack frame
    14 movl %esp, %ebp
    15 pushl %ecx
    16 subl $20, %esp
    17 movl (%ecx), %edx         #取第一个参数的值,将其放入edx寄存器
    18 sall $2, %edx             #edx算术左移两位,即乘4,这样就得到数组的大小
    19 leal 30(%edx), %eax       #以下给数组分配空间
    20 andl $-16, %eax
    21 subl %eax, %esp
    22 movl %edx, 4(%esp)        #把保存在edx中的数组的大小压栈,准备调用sprintf打印数组大小
    23 movl $.LC0, (%esp)
    24 call printf
    25 movl -4(%ebp), %ecx
    26 leave
    27 leal -4(%ecx), %esp
    28 ret
    29 .size main, .-main
    30 .ident "GCC: (Debian 4.3.2-1.1) 4.3.2"
    31 .section .note.GNU-stack,"",@progbits

看上面的注释就可以很清楚的看到,数组的大小是在运行时计算得到的,而且数组的空间也是运行时动态调整的。

情形四:结构体

#include <stdio.h>

struct t{
        char a;
        int b;
};

int
main(int argc, char **argv)
{
        struct t a;
        int b = sizeof(a);
        printf("%d\n", b);
}

gcc -O3 -S的结果:

     1 .file "t.c"
     2 .section .rodata.str1.1,"aMS",@progbits,1
     3 .LC0:
     4 .string "%d\n"
     5 .text
     6 .p2align 4,,15
     7 .globl main
     8 .type main, @function
     9 main:
    10 leal 4(%esp), %ecx
    11 andl $-16, %esp
    12 pushl -4(%ecx)
    13 pushl %ebp
    14 movl %esp, %ebp
    15 pushl %ecx
    16 subl $20, %esp
    17 movl $8, 4(%esp)
    18 movl $.LC0, (%esp)
    19 call printf
    20 addl $20, %esp
    21 popl %ecx
    22 popl %ebp
    23 leal -4(%ecx), %esp
    24 ret
    25 .size main, .-main
    26 .ident "GCC: (Debian 4.3.2-1.1) 4.3.2"
    27 .section .note.GNU-stack,"",@progbits

跟情形一和二类似,都是在17行就将结构体大小压栈。


总结
由以上的汇编程序可知,在gcc下,普通的变量,数组和结构体的大小都是在编译时就确定大小的,而可变长度数组是在运行时才能确定的。
阅读(1707) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~