出自:c专家编程.P199
在实际应用中,数组和指针可以互换的情形要比两者不可互换的情形更为常见。让我们分别考虑“声明”和“使用”这两种情况。声明本身还可以进一步分为3种情况:
·外部数组的声明;
·数组的定义(定义是声明的一种特殊情况,它分配内存空间,并可能提供一个初始值);
·函数参数的声明;
(1) extern,如extern char a[];
不能改写为指针的形式
1.声明 定义,如char a[10];
不能改写为指针的形式
数组
(2) 函数的参数,可以随意选择数组的形式或者指针的形式
2. 在表达式中使用 (1) 如c=a[i],可以随意选择数组
形式或者是指针形式
也既是:作为函数参数时、在语句或表达式中使用数组时,我们可以采用数组或者指针的任何一种形式,除此之外的其他情况下,指针和数组不要互换。下面就数组和指针相同的情况做详细的说明:
·规则1、表达式中的数组名被编译器当作一个指向该数组第一个元素的指针。
假如我们声明:
int a[10];
int *p = a;
就可以通过一下任何一种方式来访问a[i]:
p[i] *( p + i ) *( a + i ) ·····
事实上,可以采用的方法很多。对数组的引用如a[i] 在编译时总是被编译器改写成*(a+i)的形式,C语言标准要求编译器必须具备这个概念性的行为。
编译器自动把下标值的步长调整到数组元素的大小。如果整型数的长度是4个字节,那么a[i+1]和a[i]在内存中的距离就是4。对起始地址执行加法操作之前,编译器会负责计算每次增加的步长。这就是为什么指针总是有类型限制,每个指针只能指向一种类型的原因所在,因为编译器需要知道对指针进行解除引用操作时应该取几个字节,以及每个下标的步长应取几个字节。
·规则2、下标总是和指针的偏移量相同。
把数组下标作为指针加偏移量是c语言从BCPL(C语言的祖先)继承过来的技巧。在人们的常规思维中,在运行时增加对c语言下标的范围检查是不切实际的。因为取下标操作只是表示将要访问该数组,但并不保证一定要访问。而且程序员完全可以使用指针来访问数组,从而绕过下标操作符。在这种情况下,数组下标范围检测并不能检测所有对数组的访问的情况。事实上,下标范围检测被认为不值得加入到c语言当中。
还有一个说法是,在编写数组算法时,使用指针比使用数组更有效率。这个颇为人们所接收的说法在通常情况下是错误的。使用现代的产品质量优化的编译器,一维数组和指针引用所产生的代码并不具有显著的差别。不管怎样,数组下标是定义在指针的基础上,所以优化器常常可以把它转化为更有效率的指针表达式,并生成相同的机器指令。
·规则3、在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针。
在函数形参定义这个特殊情况下,编译器必须把数组形式改写成指向数组第一个元素的指针形式。编译器只向函数传递数组的地址,而不是整个数组的拷贝。这种转换意味着在声明函数的时候,以下三种形式都是合法的(同时无论实参是数组还是真的指针也都是合法的):
my_function( int *turnip ) {···}
my_function( int turnip[] ) {···}
my_function( int turnip[200] ) {···}