“指针和数组是相同的” --- 错!
注意下面两个声明的区别:
extern int *x; //声明一个指针
extern int y[]; //声明一个数组,定义在其他文件或者是全局变量
我们经常犯的错误:
In file 1:
int mango[1000];
In file 2:
extern int *mango;
file 1中定义的mango数组,到file 2中,被当成指针mango 声明。类型不匹配,编译会出现错误!
搞清楚这个问题,首先要回想一下什么叫定义,什么叫声明
定义:只能出现在一个地方,确定对象的类型并分配内存,用来创建新对象,如:int my_array[100];
声明:可以多次出现,用来描述对象的类型,用于指代其他地方定义的对象(在其他文件里,或者是全局变量) 如: extern int my_array[100]
想明白数组和指针的区别,最好的方式就是理解数组和指针的访问方式,ok, let's go!
首先,我们要理解左值和右值,其实很简单哈,别被这么多概念吓到
X = Y
X在这里就是左值,在等号左边;Y是右值,在等号右边
左值和右值在C语言中有明确的定义:左值 X 的含义是X所代表的地址,在编译时可知,左值表示存储结果的地方;右值Y 表示Y所代表的地址的内容,右值在运行时才可知,表示Y的内容。
“C语言中引入了可“修改左值”这个术语,表示左值允许出现在赋值语句的左边 例如 a = y; a = 3, a是可修改的,这个奇怪的术语是为了与数组名区分,数组名也可用于对象在内存中的位置,是左值 a[1] = 2, 但它不能作为赋值的对象 (如果a是数组名,a = 3是错误的),标准规定,只能给可以修改的东西赋值(这句话,跟人感觉很废 有木有)”。
编译器首先为左值分配一个地址,这个地址在编译时可知,而且该变量在运行时一直保存这个地址,右值只有在运行时才知道,如果需要用到变量中存储的值(右值),编译器首先发出指令从指定地址读入变量值并将它存在寄存器中。
左值表示地址,在编译时可知,所以,如果编译器需要一个地址来执行某种操作,它就可以直接运行操作,不需要增加指令首先取得具体的地址。然而,对于指针,必须首先在运行时取得他的当前值,然后才能解引用才做(指针中存的是指向对象的地址,要取得指向对象的内容,先找指针的地址,取出指向对象的地址,再取出指向对象的值).
char a[9] = "1234567890"; c = a[i];
图A:数组下标引用
9980 是基地址,运行时,编译器 :1)取i的值,将它与9980相加。 2)取地址(9980+i)的内容
所以extern char a[], extern char a[100]等价,这两个声明都提示a是一个数组,也就是一块内存地址,数组内的字符可以从这个地址找到。从一个数组中提取字符,只要简单的从符号表显示的a的基地址加上下标即可。
extern char *p 是怎么运行的呢?
编译器知道了p是一个指针,他指向的对象时一个字符,为了取得这个字符,必须得到地址p的内容,把p的内容当做一个地址,并再从这个地址中取得字符。指针访问增加了一次额外提取:
图B:指针的引用
编译器符号表中p的地址为4624,编译器 1:)取4624的内容,5018。 2)取地址5081的内容
如果定义为指针,但以数组方式引用会怎么样呢?
char *p = "abcdefgh"; c = p[i];
图C:指针下标的解引用
char *p = "abcdefgh"; -------p[3]; 图C方式
char a[] = "abcdefgh"; ------a[3]; 图A方式
但是如果char a[] = "abcdefgh" 用 extern char *p 来声明, 然后用p[3] 开引用其中的元素时会发生什么?
图C的是按照 p首先被定义为指针,然后再用p[3]来解引用,然而如果声明extern char *p, p为指针,那么不管p原来是定义为指针还是数组,都会按照图C的方式进行操作,但是只有当p原来定义为指针时,图C才能正确的找到值。
当用p[i]这种形式提取声明内容时,实际上得到是一个字符,但是按照上面的方法,编译器会把他当做一个指针,把ASCII 字符解释为地址显然不对。如果程序corrupt,是好事!
另:定义指针时,编译器并不为指针所指向的对象分配内存空间,它只分配指针本身的空间,除非定义时同时对指针一个字符串常量进行初始化: char *p= "breakdfruit";这种情况只对字符串常量有效。float *pip = 3.141; //错误!
在ANSI C 中,初始化指针时所创建的字符串常量被定义为只读,不能对字符串进行修改。然而数组定义的字符串常量可以修改。
指针是C语言的精华,需要多多学习,多多练习。从原理下手比较容易理解。
Reference:
Expert C