让人混淆的代码
char my_array[10];
char *my_ptr;
...
i = strlen(my_array);
j = strlen(my_ptr);
printf("%s,%s",my_ptr,my_array);
printf("array at location %x holds string %s",my_array,my_array);
声明,定义和函数参数声明
* 对编译器而言,数组是一个地址,指针是一个地址的地址
什么时候数组和指针时相同的?
C语言标准有3条规则:
1、表达式中的数组名被编译器当作一个指向该数组第一个元素的指针
2、下标总是和指针的偏移量相同
3、在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针
规则1
表达式中的数组名就是指针
int a[10],*p,i=2
// 1
p=a;
p[i];
// 2
p=a;
*(p+i);
// 3
p=a+i;
*p;
- 对数组的引用a[i]在编译时被编译器改写为*(a+i)的形式
- 所以a[6]和6[a]都是正确的
- 编译器会自动把下标的步长调整到数组存储的类型的大小,这也是为什么每个指针只能指向一种类型,因为编译器需要知道每次+1的偏移量,应该解引用时取多少个字节
规则2
C语言把数组下标作为指针的偏移量
* 从BCPL(C语言的祖先继承而来),因为总可以通过指针访问,所以对下标的范围检查也被认为是没必要的
* 根本原因是指针和偏移量是底层硬件所使用的基本类型
规则3
作为函数参数的数组名等同于指针
形参:在函数定义和声明中定义
实参:在实际调用的时候传递给函数的值
C语言标准要求“类型的数组”形参的声明应该调整为“类型的指针”,因此在函数形参定义的时候,编译器必须把数组形式改写为指向数组第一个元素的指针形式,编译器只向函数传递数组的地址,而不是整个数组的拷贝,隐式转换的存在意味着下述三个声明是完全等同的
void my_function(int *np);
void my_function(int np[]);
void my_function(int np[200]);
数组和指针可交换性的总结
1、用a[i]这种形式对数组进行访问总是被编译器“改写”或者解释成像“*(a+i)”这种指针访问;
2、指针始终是指针。它绝不可以改写成数组。你可以用下表形式访问指针,一般都是指针作为函数参数的时候,而且你知道实际传递给函数的是一个数组;
3、只有在把它作为函数的参数,数组的声明可以看作一个指针。作为函数参数的数组始终会被编译器修改成指向数组第1个元素的指针;
4、因此,当把一个数组定义为一个函数的参数时,可以选择把它定义为数组,也可以定义为指针。不管选择哪种方法,在函数内部事实上获得的都是一个指针;
5、在其他所有情况下,定义和声明必须匹配。如果定义了一个数组,在其他文件中进行声明时也必须是数组,指针也是如此。
Reference
C专家编程