|
发表于 2004-12-26 20:40:56
|
显示全部楼层
这个问题其实很简单,只要看看编译器产生的代码,即使不作任何说明也能分清指针和数组的区别了。
- #include <stdio.h>
- int main(int argc, char *argv[])
- {
- int a[] = {1, 2, 3};
- int *p = &a[1];
- a[1] = 4;
- *p = 4;
- printf("%d\n%d\n%d\n%p\n%p\n", a[0], *p, a[1], a, p);
- return 0;
- }
复制代码
用 gcc 编译产生的汇编代码是这样的:
- # 一些给汇编器看的内容
- .file "x.c"
- .section .rodata
- .LC0:
- .string "%d\n%d\n%d\n%p\n%p\n"
- .text
- .globl main
- .type main, @function
- # main 函数
- main:
- # main 函数的初始化
- pushl %ebp
- movl %esp, %ebp
- subl $72, %esp
- andl $-16, %esp
- movl $0, %eax
- subl %eax, %esp
- # 数组 a 的初始化。地址为:
- # a[0] <-> -24(%ebp)
- # a[1] <-> -20(%ebp)
- # a[2] <-> -16(%ebp)
- # 注意数组元素的访问方式
- movl $1, -24(%ebp)
- movl $2, -20(%ebp)
- movl $3, -16(%ebp)
- # 指针 p 的初始化:共三步
- # 取数组 a 的起始地址
- leal -24(%ebp), %eax
- # 取 a[1] 的地址
- addl $4, %eax
- # 为指针 p 赋值
- movl %eax, -28(%ebp)
- # a[1] = 4
- movl $4, -20(%ebp)
- # *p = 4:共两步
- # 取指针 p 的地址
- movl -28(%ebp), %eax
- # 移入指针 p 指向的地址
- # 注意寻址方式
- movl $4, (%eax)
- # 下面准备调用 printf
- # 参数:从左至右压入堆栈但写入的顺序相反
- # 首先压入指针 p 指向的地址
- movl -28(%ebp), %eax
- movl %eax, 20(%esp)
- # 再压入数组 a 的起始地址
- leal -24(%ebp), %eax
- movl %eax, 16(%esp)
- # 压入 a[1]:只一步
- movl -20(%ebp), %eax
- movl %eax, 12(%esp)
- # 压入 *p:分两步
- # 首先取出指针 p 指向的地址
- movl -28(%ebp), %eax
- # 然后压入这个地址中的内容
- # 注意寻址方式
- movl (%eax), %eax
- movl %eax, 8(%esp)
- # 压入 a[0]:只一步
- movl -24(%ebp), %eax
- movl %eax, 4(%esp)
- # 参数全部压入堆栈,调用 printf
- movl $.LC0, (%esp)
- call printf
- # 准备从 main 返回,return 0
- movl $0, %eax
- leave
- ret
- # 其他一些内容
- .size main, .-main
- .section .note.GNU-stack,"",@progbits
- .ident "GCC: (GNU) 3.3.4 (Debian 1:3.3.4-13)"
复制代码
我加了些注释,同时也说明了 C 中的指针和数组是如何被使用的。可以看到的是,数组元素的大小在编译时确定,并被写入到汇编代码中。这样,就可以用诸如 -24(%ebp) 和 -20(%ebp) 这样的形式访问数组元素而不必担心数据被覆盖,并且可以使用像 addl $4, %eax 这样的汇编指令。读了这些汇编指令,就能够彻底明白数组与指针的区别了吧。 |
|