以前写过一篇阐述C语言中指针和数组区别的文章,最近很后知后觉地读《C专家编程》时发现居然有三章是在详细说这个问题,读完后算是把这个问题搞得更透彻了。遂用一组C源码和汇编码的对比来再次展示它们的区别。
(试验环境:Ubuntu Linux/GCC)
源码(test.c):
int fun1() { int a[1],b[1],*p=b; a[0]=0; *(p+0)=1; return 0; }
免优化汇编码(gcc -S test.c):
.file "test.c" .text .globl fun1 .type fun1, @function fun1: pushl %ebp movl %esp, %ebp subl $16, %esp leal -8(%ebp), %eax movl %eax, -12(%ebp) movl $0, -4(%ebp) movl -12(%ebp), %eax movl $1, (%eax) movl $0, %eax leave ret .size fun1, .-fun1 .ident "GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3" .section .note.GNU-stack,"",@progbits
通过汇编码可以清晰地看到,当如果数组是通过a[0]这种形式定义的,那么在访问它时数组名会直接被转成地址(不经过二次寻址过程),而下标就是偏移量:movl $0, -4(%ebp)。而若通过指针去操作数组,则会经历二次寻址:先把指针中的值取出(movl -12(%ebp), %eax),再通过这个值加偏移量去访问目标元素(movl $1, (%eax))。即在这种情况下,对数组和指针的访问机理是不同的。
还有另外一种情况,就是数组被当作参数传递给子程序,比如:
void fun(int a[]) { a[0]=1; }
汇编码为:
.file "test.c" .text .globl fun .type fun, @function fun: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax movl $1, (%eax) popl %ebp ret .size fun, .-fun .ident "GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3" .section .note.GNU-stack,"",@progbits
我们可以看到,当数组被当作参数传给子程序后,实际上是被转换成了指针,在子函数中堆元素的操作也是通过了“二次取址”过程。