今天跟同事争论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下,普通的变量,数组和结构体的大小都是在编译时就确定大小的,而可变长度数组是在运行时才能确定的。
阅读(1749) | 评论(0) | 转发(0) |