声明本身还可以进一步分成3种情况:
•外部数组(external array)的声明。
•数组的定义(记住,定义是声明的一种特殊情况,它分配内存冷间,许可能提供一个 初始值)。
•函数参数的声明。
所有作为函数参数的数组名总是可以通过编译器转换为指针。在其他所有情况卜(最 有趣的情况就是“在一个文件中定义为数组,在另一个文件中声明为指针”),数组的声明就是数组,指针的声明就是指针,两者不能浞淆。但在使用数组(在语 句或表达式中引用)时,数组总是町以写成指针的形式,两者可以瓦换。图9-1对这牲情 况作了总结。
然而,数组和指针在编译器处理时是不同的,在运行时的表示形式也是不一样的,并可能产生不同的代码。对编译器而言,一个数组就是一个地址,一个指针就是一个地址的地址。 你应该根据情况做出选择。
什么时候数组和指针是相同的
C语言标准对此作了如下说明:
规则1.表达式中的数组名(与声明不同)被编译器当作一个指向该数组第一个元素的指针
规则2.下标总是与指针的偏移量相同。
规则3.在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针。
规则1: “表达式中的数组名”就是指针
上面的规则1和规则2合在一起理解,就是对数组下标的引用总足可以成“ 一个指向数组的起始地址的指针加上偏移量”。例如,假如我们声明:
事实上,可以采用的方法更多。对数组的引用如a[i]在编译时总是被编译器改写成*(a+i) 的形式。C语言标准要求编译器必须具备这个概念性的行为。也许遵循这个规则的捷径就是记住方括号[]表示一个取下标操作符,就像加号表示一个加法运箅符一样。取下标操作符取一个整数和一个指向类型T的指针,所产生的结果类型是T,一个表达式中的数组名于是就成了指针。你只要记住:在表达式中,指针和数组是可以亙换的,因为它们在编译器里的最终形式都是指针,并且都可以进行取下标操作。就像加法一样,取下标操作符的操作数是可以交换的(它并不在意操作数的先后顺序,就像在加法中3+5和5+3并没有什么不一样)。 这就是为什么在一个a[10]的声明中下面两种形式都是正确的:int a[10], *p, i = 2;
就可以通过以K任何一种方法来访问a[i]:
在实际的产品代码中,上面笫二种形式从来不曾使用。确实,它除了可以把新手搞晕之外,实在没有什么实际意义。a[10]=......;
10[a]=......;
编译器自动把下标值的步长调整到数组元素的人小。如果整型数的长度是4个字节,那 么a[i+l]和a[i]在内存中的距离就是4 (而不是1)。对起始地址执行加法操作之前,编译器会负责计算每次增加的步长。这就是为什么指针总是有类型限制,每个指针只能指向一种类型的原因所在一因为编译器需要知道对指针进行解除引用操作时应该取几个字节,以及每个下标的步长应取几个字节。