汇编角度分析 c 语言中指针与数组的区别
数组与指针是两个不同的概念,即使是从编译的层面上来看。不过,访问和操作数组的行为和指针在用法上极为相似。
数组的首地址与指针的值,可以认为等同,编译器能直接得到数组的首地址, 但要得到指针的值, 必须先得到指针的地址.从而, 通过指针访问数组通常要慢一点,当然是
lea (a), %esi /* this is a */ mov (%esi), %eax /* this is a[0] */ 或更简单的: mov (a), %esi /* this is a[0] */
微不足道的.
对于指针,
int *p;
p 是一个变量,所以编译器要为之分配一个空间。
.comm p, 4
对于数组:
int a[10]
a 是一个地址,编译器会为数组 a 分配一个空间,但不会为 a 本身分配空间,在使用到a的地方,会被替换为一个地址+属性,其结果为一个"常量指针"。
.comm a, 40
lea (p), %esi /* this is &p */ mov (%esi), %edi /* this is p */ mov (%edi), %eax /* this is p[0] */ 或者,更简单的 mov (p), %esi /* this is p */ mov (%esi), %eax /* and this is p[0] */
相比之下,数组的引用
int a[10]; a[0];
则省去了取 a 地址的过程,符号 a 代表一个地址,这个地址不存放在任何变量中!
lea (a), %esi /* this is a */ mov (%esi), %eax /* this is a[0] */ 或更简单的: mov (a), %esi /* this is a[0] */
摘录二:数组名、指针、地址这几个概念的轮回解析
数组是指针的基础,多数人就是从数组的学习开始指针的旅程的。下面我节选一些在各种论坛和文章里经常见到的关于数组的文字: “一维数组是一级指针” “二维数组是二级指针” “数组名可以作为指针使用” “数组名就是..........的常量指针” “数组名就是..........的指针常量” .................................. 这些文字看起来非常熟悉吧?类似的文字还有许多,或许你就是经常说这些话的人呢。不过非常遗憾,这些文字都是错误的,实际上数组名永远都不会是指针!这个结论也许会让你震惊,但它的确是事实。
数组名、指针、地址这几个概念虽然是基础中的基础,但它们恰恰是被混淆和滥用得最多的概念,把数组名说成指针,是一个概念性的错误,实质是混淆了指针与地址两个概念的本质。俗话说得好:浅水淹死人。因此,在讨论数组之前,有必要先回过头来澄清一下什么是指针,什么是地址,什么是数组名。 指针是C语言具有低级语言特征的最直接的证据。在汇编语言里面,指针的概念随处可见。比如SP,SP寄存器又叫堆栈指针,它的值是地址,由于SP保存的是地址,并且SP的值是不断变化的,因此可以看作一个变量,而且是一个地址变量。地址也是C语言指针的值,C语言的指针跟SP这样的寄存器虽然不完全一样,但原理却是相通的。C语言的指针也是一种地址变量,C89明确规定,指针是一个保存对象地址的变量。这里要注意的是,指针跟地址概念的不同,指针是一种地址变量,通常也叫指针变量,统称指针。而地址则是地址变量的值。
看到这里,也许你会觉得,这么简单的东西还用你来说吗?的确,对于p与&p来说,99%的人都能在0.1秒内脱口而出谁是指针,谁是地址,但是,又有多少人在使用指针的过程中能够始终如一毫不动摇地遵循这两个概念呢?不少人使用指针的时候就会自觉或不自觉地把指针和地址两个概念混淆得一塌糊涂了,数组名的滥用就是一个活生生的例子。这一点甚至连一些经典著作也没能避免。
不过也不能全怪你自己,笔者认为某些国内教材应该承担最大的责任。这些教材一开始就没有给读者好好地分清指针与地址的区别,相反还在讲述的过程中有意无意地混用这两个概念。更有甚者,甚至在书中明言指针就是地址!说这话的家伙最应该在C语言这个地图上抹掉,呵呵。两个月前我在购书中心随手翻开了某个作者主编的一本被冠以国家“十五”规划重点研究项目的书,书里就是这么写的。当时笔者就感慨:不知道又要有多少人的思想被这家伙“强奸”了。
实际上,地址这个东西,本来就是一种基本数据类型,本应该在介绍整数、浮点、字符等基本类型的时候把地址显式地放在一起讨论,这样在后面介绍指针与数组的时候就能避免许多误解。可惜不少教材或者根本没有谈及,或者就算提起这个类型也用了指针类型这个字眼。这就错了,指针不是类型,真正的类型是地址,指针只是存储地址这种数据类型的变量!打个比方,对于 int i=10; 10是整数,而i是存储整数的变量,指针就好比这个i,地址就好比那个10。指针能够进行加减法,原因并不是因为它是指针,加减法则不是属于指针这种变量的,而是地址这种数据类型的本能,正是因为地址具有加减的能力,所以才使指针作为存放地址的变量能够进行加减运算。这跟整数变量因为整数能够进行加减乘除因而它也能进行加减乘除一个道理。
那么数组名又应该如何理解呢?用来存放数组的区域是一块在栈中静态分配的内存(非static),而数组名是这块内存的代表,它被定义为这块内存的首地址。这就说明了数组名是一个地址,而且,还是一个不可修改的常量,完整地说,就是一个地址常量。数组名跟枚举常量类似,都属于符号常量。数组名这个符号,就代表了那块内存的首地址。注意了!不是数组名这个符号的值是那块内存的首地址,而是数组名这个符号本身就代表了首地址这个地址值,它就是这个地址,这就是数组名属于符号常量的意义所在。
由于数组名是一种符号常量,因此它是一个右值,而指针,作为变量,却是一个左值,一个右值永远都不会是左值,那么,数组名永远都不会是指针!不管什么话,只要说数组名是一个指针的,都是错误的!就象把刚才int i=10例子中的10?党墒钦淞恳谎谧罨镜牧⒆愕闵暇鸵丫甏砹恕? 总之要牢牢记住,数组名是一个地址,一个符号地址常量,不是一个变量,更不是一个作为变量的指针!