第四章 令人震惊的事实:数组和指针并不相同
4.1 数组并非指针
extern int *x;
extern int y[];
4.2 我的代码为什么无法运行
文件1:
int mango[100];
文件2:
extern int*mango;
并不能正确运行
4.3 什么是声明,什么是定义
定义,只能出现在一个地方,确定对象的类型并分配内存,用于创建新的对象。例如:int my_array[100];
声明,可以出现多次,描述对象的类型,用于指代其他地方定义的对象(例如在其他文件里)。例如:extern int my_array[[];由于并未在声明中为数组分配内存,所以并不需要提供数组长度的信息。对于多维数组,提供除最左边一维之外其他维的长度——这就是给编译器足够的信息产生相应的代码。
4.3.1 数组和指针是如何访问的
X = Y;
X的含义是X所代表的地址。
这个被称为左值。
左值在编译时可知,左值表示存储结果的地方。
Y的含义是Y所代表的地址的内容。
这个被称为右值。
右值知道运行时才知。如无特别说明,右值表示“Y的内容”。
每个符号的地址在编译时可知。所以,如果编译器需要一个地址(可能还需要加上偏移量)来执行某种操作,它就可以直接进行操作,并不需要增加指令首先取得具体的地址。相反,对于指针,必须首先在运行时取得它的当前值,然后才能对它进行解除引用操作(作为以后进行查找的步骤之一)。图A展示了对数组下标的引用
如果声明extern char *p,它将告诉编译器p是一个指针,它指向的对象是一个字符。为了取得这个字符,必须取得p的内容,把它作为字符的地址并从这个地址中取得这个字符。
4.3.2 当你“定义为指针,但以数组方式引用”时会发生什么
当书写了extern char *p,然后用p[3]来引用其中的元素时,编译器将会:
1. 取得符号表中p的地址,提取存储于此处的指针。
2. 把下标所表示的偏移量与指针的值相加,产生一个地址。
3. 访问上面这个地址,取得字符。
4.4 使声明与定义相匹配
4.5 数组和指针的其他区别
定义指针时,编译器并不为指针所指向的对象分配空间,它只是分配指针本身的空间,除非在定义时同时赋给指针一个字符串常量进行初始化。
只有字符串常量才是如此。不能指望为浮点数之类的常量分配空间,如:
float *pip = 3.141; /*错误!无法通过编译*/
在ANSI C中,初始化指针时所创建的字符串常量被认为只读。如果试图通过指针修改这个字符串的值,程序会出现未定义的行为。在某些编译器中,字符串常量被存放在只允许读取的文本段中,以防止它被修改。
由字符串常量初始化的数组是可以修改的。
4.6 轻松一下——回文的乐趣