为什么会引起混乱?
首先,请允许我强调一下本章的重要观点。
C语言的数组和指针是完全不同的。
大家都说C语言的指针比较难,可是真正地让初学者“挠墙”的,并不是指针自身的使用,而是“混淆了数组和指针”。此外,很多“坑爹”的入门书对指针和数组的讲解也是极其混乱。
比如,K&R中就有下面一段文字(p.119)
C语言的指针和数组之间有很强的关联关系,因此必须将指针和数组放在一起讨论。
很多C程序员认为“数组和指针是几乎相同的事物”,这种认识是引起C的混乱的主要原因。
从图3-17中可以一目了然地看出,数组是一些对象排列后形成的,指针则表示指向某处。他们是完全不同的。
带着“数组和指针是几乎相同的事物”这样的误解,初学者经常写出下面这样的代码:
int *p;
p[3] = …… ←突然使用没有指向内存区域的指针
——自动变量的指针在初期状态,值是不定的。
char str[10];
……
……
……
str = “abc”; ←突然想数组赋值
——数组既不是标量,也不是结构体,不能临时使用。
int p[]; ←使用空的[]声明本地变量
——只有在“函数的形参的声明”中,数组的声明才可以被解读成指针。
对于数组和指针,他们在哪些地方是相似的,又在那些地方是不同的?不好意思,可能在下面会出现和前面重复的内容。
表达式之中
在表达式中,数组可以被解读成指向其初始元素的指针。所以,可以写成下面这样:
int array[10];
p = array; ←将指向array[0]的指针赋予p
可是,反过来写成下面这样
array = p;
就是不可以的。却是,在表达式中array可以被解读成指针,可是本质上他其实是被解释成了&array[0],此时的指针是一个右值*。(此时的指针是右值这个理由之外,在标准中,数组也不是“可变更的左值”。)
比如,对于int类型的变量a , a = 10;这样的赋值是可以的,单肯定没有人想做a + 1 = 10;这样的赋值吧。尽管a 和 a + 1都是int,但是a + 1没有对应的内存区域,只是一个右值,所以不能被赋值。同样的道理,array也不能被赋值。此外,对于下面这个指针,
int *p;
如果p指向了某个数组,自然可以通过p[i]的方式进行访问, 单着并不代表p就是指针。
p[i]只不过是*(p + i)的语法糖,只要p正确地指向一个数组,就可以通过p[i]对数组的内容进行访问,如图3-18所示。
如果是“指针的数组”和“数组的数组”,就会有很大的不同。
char *color_name[] = {
“red”,
“green”,
“blue”,
};
对以上的代码进行图解(参照图3-19),
char color_name[][6] = {
“red”,
“green”,
“blue”,
}
对以上的代码进行图解(参照图3-20),
以上两种情况都可以用color_name[i][j]的方式对数组进行访问,但是内存中数据的布局是完全不同的。
声明
只有在声明函数的形参的时候,数组的声明才能解读成指针的声明(参照3.5.1节)。
以上的语法糖,与其说使C变得更加容易理解,倒不如说它使C语言的语法变得更加混乱。是不是有很多人这么想?我就是其中的一个(虽然使用这个语法糖可以让多维数组作为参数被传递时更容易理解。)而且 K&R的说明更是使这种混乱局面雪上加霜。
在不是声明函数的形参的时候,数组声明和指针的声明是不可能相等的。
使用extern的时候是最容易出问题的(参照3.5.2节)。另外,声明局部变量或者结构体的成员时候,写成
int hoge[];
会引起语法错误(对于结构体的成员,在ISO C99中是允许这种写法的)。
存在数组初始化表达式的情况下,可以使用空的[],但这是因为编译器能够计算出数组元素的个数,所以可以省略书写元素的个数。仅此而已,这种特征和数组扯不上任何关系。
要点
【非常重要!!】
数组和指针是不同的事物。
内容摘自:《征服C指针》
//本文转自SinaAppEngine微博