转载:http://blog.csdn.net/walkinginthewind/article/details/7044380
C语言中数组和指针是一种很特别的关系,首先本质上肯定是不同的,本文从各个角度论述数组和指针。
一、数组与指针的关系
数组和指针是两种不同的类型,数组具有确定数量的元素,而指针只是一个标量值。数组可以在某些情况下转换为指针,当数组名在表达式中使用时,编译器会把数组名转换为一个指针常量,是数组中的第一个元素的地址,类型就是数组元素的地址类型,如:
int a[5]={0,1,2,3,4};
数组名a若出现在表达式中,如int *p=a;那么它就转换为第一个元素的地址,等价于int *p=&a[0];
再来一个:
int aa[2][5]={0,1,2,3,4,
5,6,7,8,9};
数组名aa若出现在表达式中,如int (*p)[5]=aa;那么它就转换为第一个元素的地址,等价于int (*p)[5]=&aa[0];
但是int (*p)[5]=aa[0]; 这个就不对了,根据规则我们推一下就很明了了,aa[0]的类型是int [5],是一个元素数量为5的整型数组,就算转化,那么转化成的是数组(int [5])中第一个元素的地址&aa[0][0],类型是 int *。所以,要么是int (*p)[5]=aa;要么是int (*p)[5]=&aa[0];
只有在两种场合下,数组名并不用指针常量来表示--就是当数组名作为sizeof操作符或单目操作符&的操作数时,sizeof返回整个数组的长度,使用的是它的类型信息,而不是地址信息,不是指向数组的指针的长度。取一个数组名的地址所产生的是一个指向数组的指针,而不是指向某个指针常量值的指针。
如对数组a,&a表示的是指向数组a的指针,类型是int (*) [5],所以int *p=&a;是不对的,因为右边是一个整形数组的指针int (*)[5],而p是一个整形指针int *;
数组的sizeof问题会在下面中仔细讨论。
二、数组与指针的下标引用
int a[5]={0,1,2,3,4};
如a[3],用下标来访问数组a中的第三个元素,那么下标的本质是什么?本质就是这样的一个表达式:*(a+3),当然表达式中必须含有有效的数组名或指针变量。
其实a[3]和3[a]是等价的,因为他们被翻译成相同的表达式(顶多顺序不同而已),都是访问的数组a中的元素3。
指针当然也能用下标的形式了,如:int *p=a; 那么p[3]就是*(p+3),等同于3[p](不要邪恶。。。3P,3P),同样访问数组a中的元素3。
根据这一规则,我们还能写出更奇怪的表达式,如:
int aa[2][5]={0,1,2,3,4,
5,6,7,8,9};
1[aa][2],这个看起来很别扭,首先 1[aa],就是*(1+aa),那么1[aa][2]就是*(*(1+aa)+2),也就是aa[1][2]。
1[2][aa],这个就不对了,因为前半部分1[2]是不符合要求的。
当然在实际中使用这样的表达式是没有意义的,除非就是不想让人很容易的看懂你的代码。
三、数组与指针的定义和声明
数组和指针的定义与声明必须保持一致,不能一个地方定义的是数组,然后再另一个地方声明为指针。
首先我们解释一下数组名的下标引用和指针的下标应用,它们是不完全相同的,从访问的方式来讲。
int a[5]={0,1,2,3,4};
int *p=a;
对于a[3]和p[3]都会解析成*(a+3)和*(p+3),但是实质是不一样的。
首先对于a[3],也就是*(a+3):
(1)把数组名a代表的数组首地址和3相加,得到要访问数据的地址,这里注意,数组名a直接被编译成数组的首地址;
(2)访问这个地址,取出数据。
对于p[3],也就是*(p+3):
(1)从p代表的地址单元里取出内容,也就是数组首地址,指针名p代表的是指针变量的存储地址,变量的地址单元里存放的才是数组的首地址;
(2)把取出的数组首地址和3相加,得到要访问的数据的地址;
(3)访问这个地址,取出数据。
下面给出一个例子来说明若定义和声明不一致带来的问题:
设test1.cpp中有如下定义:
char s[]="abcdefg";
test2.cpp中有如下声明:
extern char *s;
显然编译是没有问题的。
那么在test2.cpp中引用s[i]结果怎样呢?如s[3],是‘d’吗?好像是吧
下面我们对test2.cpp中的s[3]进行分析:
s的地址当然是由test1.cpp中的定义决定了,因为在定义时才分配内存空间的;
我们根据上面给出的指针下标引用的步骤进行计算
(1)从s代表的地址单元的取出内容(4个字节),这里实际上是数组s中的前4个元素,这个值是“abcd”,也就是16进制64636261h,到这一步应该就能看出来问题了;
(2)然后把取出的首地址和3相加,得到要访问的数据的地址64636261h+3,这个地址是未分配未定义的;
(3)取地址64636261h+3的内容,这个地址单元是未定义的,访问就会出错。
下面给出分析的代码(可只需观察有注释的部分):
#include<iostream> using namespace std; extern void test(); char s[]="abcdefg"; int main() { 002E13A0 push ebp 002E13A1 mov ebp,esp 002E13A3 sub esp,0D8h 002E13A9 push ebx 002E13AA push esi 002E13AB push edi 002E13AC lea edi,[ebp+FFFFFF28h] 002E13B2 mov ecx,36h 002E13B7 mov eax,0CCCCCCCCh 002E13BC rep stos dword ptr es:[edi] char ch; int i=3; 002E13BE mov dword ptr [ebp-14h],3 ch = s[i]; 002E13C5 mov eax,dword ptr [ebp-14h] 002E13C8 mov cl,byte ptr [eax+011F7000h] /* s直接翻译成数组首地址和i(eax)相加,得到操作数地址,然后作为byte ptr类型取内容,传给cl */ 002E13CE mov byte ptr [ebp-5],cl /* cl的内容传给ch(ebp-5) */ test(); 002E13D1 call 002E1073 return 0; 002E13D6 xor eax,eax } 002E13D8 pop edi 002E13D