3.6 应该记住:数组和指针是不同的事物

3.6.1 为什么会引起混乱

首先,请允许我强调一下本章的重要观点。

C 语言的数组和指针是完全不同的。

大家都说 C 语言的指针比较难,可是真正地让初学者“挠墙”的,并不是指针自身的使用,而是“混淆了数组和指针”。此外,很多“坑爹”的入门书对指针和数组的讲解也是极其混乱。

比如,K&R 中就有下面一段文字(p.119),

C 语言的指针和数组之间有很强的关联关系,因此必须将指针和数组放在一起讨论。

很多 C 程序员认为“数组和指针是几乎相同的事物”,这种认识是引起 C 的混乱的主要原因。

从图 3-17 中可以一目了然地看出,数组是一些对象排列后形成的,指针则表示指向某处。它们是完全不同的。

3.6 应该记住:数组和指针是不同的事物 - 图1

图 3-17 数组和指针

带着“数组和指针是几乎相同的事物”这样的误解,初学者经常写出下面这样的代码:

  1. int *p;
  2. p[3] = …… ←突然使用没有指向内存区域的指针

——自动变量的指针在初期状态,值是不定的。

  1. char str[10];
  2. str = "abc"; ←突然向数组赋值

——数组既不是标量,也不是结构体,不能临时使用。

  1. int p[]; ←使用空的[]声明局部变量

——只有在“函数的形参的声明”中,数组的声明才可以被解读成指针。

对于数组和指针,它们在哪些地方是相似的,又在哪些地方是不同的——不好意思,可能在下面会出现和前面重复的内容。

3.6.2 表达式之中

在表达式中,数组可以被解读成指向其初始元素的指针。所以,可以写成下面这样:

  1. int *p;
  2. int array[10];
  3. p = array; ←将指向array[0]的指针赋予p

可是,反过来写成下面这样:

  1. array = p;

就是不可以的。确实,在表达式中 array 可以被解读成指针,可是,本质上它其实是被解释成了&array[0],此时的指针是一个右值*

* 此时的指针是右值这个理由之外,在标准中,数组也不是“可变更的左值”。

比如,对于 int 类型的变量 aa = 10;这样的赋值是可以的,但肯定没有人想做 a + 1 = 10;这样的赋值吧。尽管 aa + 1 都是 int,但是 a + 1 没有对应的内存区域,只是一个右值,所以不能被赋值。同样的道理,array 也不能被赋值。

此外,对于下面这个指针,

  1. int *p;

如果 p 指向了某个数组,自然可以通过 p[i]的方式进行访问,但这并不代表 p 就是数组。

p[i]只不过是*(p + i)的语法糖,只要 p 正确地指向一个数组,就可以通过 p[i]对数组的内容进行访问,就像图 3-18 表现的这样。

3.6 应该记住:数组和指针是不同的事物 - 图2

图 3-18 使用指针访问数组

如果是“指针的数组”和“数组的数组”,就会有很大的不同。

  1. char *color_name[] = { ←指针的数组
  2. "red",
  3. "green",
  4. "blue",
  5. };

对以上的代码进行图解(参照图 3-19),

3.6 应该记住:数组和指针是不同的事物 - 图3

图 3-19 指针的数组

  1. char color_name[][6] = { ←数组的数组
  2. "red",
  3. "green",
  4. "blue",
  5. };

对以上的代码进行图解(参照图 3-20),

3.6 应该记住:数组和指针是不同的事物 - 图4

图 3-20 数组的数组

以上两种情况都可以用 color_name[i][j]的方式对数组进行访问,但是内存中数据的布局是完全不同的。

3.6.3 声明

只有在声明函数的形参的时候,数组的声明才能解读成指针的声明(参照 3.5.1 节)。

以上的语法糖,与其说使 C 变得更加容易理解,倒不如说它使 C 语言的语法变得更加混乱。是不是有很多人这么想?我就是其中的一个*。而且 K&R 的说明更是使这种混乱局面雪上加霜。

* 虽然使用这个语法糖可以让多维数组作为参数被传递时更容易理解……

在不是声明函数的形参的时候,数组声明和指针的声明是不可能相等的。

使用 extern 的时候是最容易出现问题的(参照 3.5.2 节)。另外,声明局部变量或者结构体的成员时,写成

  1. int hoge[];

会引起语法错误*

* 对于结构体的成员,在 ISO C99 中是允许这种写法的。

存在数组初始化表达式的情况下,可以使用空的[],但这是因为编译器能够计算出数组元素的个数,所以可以省略书写元素个数。仅此而已,这种特征和数组扯不上任何关系。

要 点

【非常重要!!】

数组和指针是不同的事物。