在很多人眼里,C语言的指针和数组是等价的。几乎在任何时候数组都可以指针来替换,这使得很多人把这两个类型视作等价。其实完全不同。数组中存的是数据,指针中存的是地址。我们造成这样的误解是有情可缘的,对于int *a而言,a是一个变量,这个变量的值是一个4字节整数的地址。而对于int a[10]而言,我们常常可以在一些书上看到a指向数组的第一个元素,似乎它也是个指针变量,变量的值是a[0]的地址。这与int *a似乎完全一样。但其实不然:看下面一段程序:翻译出的汇编可以看到 movl $4, -24(%ebp) 对于一个数组char a[10]而言,如果访问a[i],步骤是: 1. 将a的地址读入寄存器,由于a其实就是一个别名,所以a的地址本身就是a[0]的地址 2. 根据类型计算并添加偏移,得到目标地址 3. 访问内存得到a[i] 而如果声明是char *a,访问a[i],其步骤是: 1. 将a的地址读入寄存器,a是一个指针,指向a[0]的地址。 2. 访问内存读取a的值,得到a[0]的地址。 3. 根据类型计算并添加偏移量,得到目标地址 4. 访问内存得到a[i] 根据这样的理解,当我们在一个文件中声明变量为char *a,而在另一个文件中将a定义为数组,即char a[10],将会造成非常大的问题。在声明变量的文件中使用a[i],将首先会把a的地址读入寄存器,访问内存取出a的值,而由于其定义为数组,因此a的地址就是a[0]的地址,将取出的a[0]作为目标地址的基址显然是错误的。 那么肯定有人会问,int *p = a算是个什么东西,不是说好的a是个别名而已么,a的地址才是a[0]的地址啊。其原因是,C标准中说道,在表达式中使用数组,编译器会将数组自动转化成指针,这是所有C编译器必须有的行为,该指针指向数组的第一个元素。 需要注意的是,对于指针的下标操作[],当且仅当其定义是数组的时候才有效,对于指针类型的声明,编译器将下标操作直接作为加号操作,因此a[5]和5[a]在a为指针的时候都是合法的。 可能又会有人有疑问,常常看到一些函数的签名是指针,可传入的是数组,不也没事儿么。C标准还有一条:当数组出现在函数的参数中时,无论是形参还是实参,都转换为指针再进行操作。因此main函数的参数char *argv[],也可以写成char **argv. 但这种转换不是在任何时候都成立的,比如多维数组的情形,char a[3][4][5]作为参数传递的时候只会转换为一个3维数组的指针char (*a)[4][5],而不会转成指针的指针的指针。 函数不可以返回数组,但可以返回数组的指针,比如:
- int a[10];
- int main(){
- int *p = a;
- p[5] = 4;
- return p[5];
- }
- int (*func())[]{
- ...
- }
《C专家编程》的笔记-指针与数组的区别
最新推荐文章于 2021-07-14 22:49:33 发布