数组与指针的复杂性让人望而生畏,不是一朝一夕就能完全掌握的,需要一段较长的时间慢
慢消化。因此笔者才将数组与指针称为一门艺术,是的,它就是艺术!
第二章 数组名是一个指针常量吗?
数组名是一个指针常量这种观点来源于数组名在表达式计算中与指针的结果等效性。例如下
面的代码:
int a[10], *p = a, *q;
q = a + 1;
q = p + 1;
在效果上看,a + 1 与 p + 1 是相同的,这很容易给人一种 a 就是 p 的假象,但,这仅仅是假象。
鉴于指针常量包含了指针和常量两类概念,我们可以把这个问题分开两部分进行讨论。
一、数组名是指针吗?
在《C与指针》一书中,作者用一个著名的例子阐述了数组名与指针的不同。在一个文件中
定义:int a[10];然后在另一个文件中声明:extern int *a; 笔者不在这里重复其中的原理,书中
的作者试图从底层操作上阐述数组名与指针的不同点,但笔者认为这个例子存在一些不足,a
在表达式中会转换为一个非对象的符号地址,而指针 a 却是一个对象,用一个非对象去跟一
个对象比较,有“偷跑”的嫌疑,这个例子只是说明了数组名的非对象性质,只能证明对象与
非对象实体在底层操作上的不同,事实上,如上一章所述,指针也有非对象形态。笔者认为,
无须从底层的角度上花费那么多唇舌,仅仅从字面上的语义就可以推翻数组名是一个指针的
观点。
首先,在C/C++中,数组类型跟指针类型是两种不同的派生类型,数组名跟指针是两种
不同类型的实体,把数组类型的实体说成“是”另一个类型的实体,本身就是荒谬的;
其次,a + 1 在效果上之所以等同于 p + 1,是因为 a 进行了数组到指针的隐式转换,这是一个
转换的过程,是 converted to 而不是 is a 的过程。如果是两个相同的事物,又怎会有转换的过
程呢?当把 a 放在 a + 1 表达式中时,a 已经从一个数组名转换为一个指针,a 是作为指针而
不是数组名参与运算的;
第三,a + 1 与 p + 1 是等效关系,不是等价关系。等价是相同事物的不同表现形式,而等效
是不同事物的相同效果。把数组名说成是指针实际上把等效关系误解为等价关系。
因此,数组名不是指针,永远也不是,但在一定条件下,数组名可以转换为指针。
二、数组名是一个常量吗?
看见这句话有人会觉得奇怪,数组定义之后就不能改变了,数组名不就是个常量吗?在表达
式中,数组名的确可以转换为一个不变的符号地址,但在C中,不变的实体不一定是常量!
而且,C/C++有常量与常量表达式之分,常量与常量表达式是两种不同的实体,但常量表达
式可以作为常量使用。C/C++中的常量虽然有所不同,但都不包括数组或数组名,而且数组
名也不一定是常量表达式。
请在 C90 的编译器中编译如下代码,注意不能是 C99 和 C++的,因为 C99 和 C++不再规定数
组的初始化器必须是常量表达式,会看不到效果:
int main( void )
{
static int a[10], b[10];
int c[10], d[10];
int* e[] = { a, b }; /* A */
int* f[] = { c, d }; /* B */
return 0;
}