UP | HOME

数组与指针

目录

数组与指针虽然非常类似,但是却不完全相同。但是很多人都有这么一个错误的认识,即数组与指针是相同的,这就导致了在使用指针时出现各样的错误。

数组与指针的不同

当定义为数组,并且以数组的方式引用时,实际上是这样的:

array.png

每个符号的地址在编译时可知,所以直接取得 a 的地址,然后再将这个地址加上 i 得到所需下标的地址,最后根据计算出的地址取得数组下标 i 指向的内容。


而当定义为指针时,是这样:

point.png

而指针必须首先在运行时取得其存储的地址,再根据这个地址取得实际存储的内容。由此可知虽然指针要灵活的多,但需要增加一次额外的提取。


因此,当定义为指针,但却以数组的方式引用时,实际上是这样的:

array_as_point.png

这是编译器所执行的是间接引用,因为我们告诉编译器我们拥有的实际是一个指针。正式的来说,编译器会:

  1. 取得符号表 p 的地址,提取存储于此处的指针的值。
  2. 把下标所表示的偏移量与指针的值相加,产生一个地址。
  3. 访问上面这个地址,取得字符。

下面我们来考虑一个例子,假设 p 被声明为 extern char *p ,但其实际的定义却是数组的形式 char p[10] 。则当我们使用 p[i]时,我们想要得到的是数组中第 i 个元素——一个字符。但是按照上面的说法,编译器却将存储在 p[0]的字符解释为一个地址,然后再将这个字符加上 i 产生一个地址,并取得这个地址的值。显示这是不正确的,它将 ACSII 字符解释为了一个地址,如果它能正常运行会取得一个不知道是会是哪里的值。还好一般编译器会提出一个 error —— ”conflicting types for ‘p'”。

所以,数组的声明必须是数组,指针的声明必须是指针,两者不能混淆。不过在使用数组时,还有作为函数参数的声明时,数组总是可以写成指针的形式,两者可以互换。


数组与指针的相同

C 语言标准对此作了如下说明:

  • 规则 1. 表达式中的数组名(与声明不同)被编译器当作一个指向该数组第一个元素的指针(具体释义见 ANSI C 标准第 6.2.2.1 节)。
  • 规则 2. 下标总是与指针的偏移量相同(具体释义见 ANSI C 标准第 6.3.2.1 节)。
  • 规则 3. 在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针(具体释义见 ANSI C 标准第 6.7.1 节)。

在表达式中,指针和数组是可以互换的,因为它们在编译器里的最终形式都是指针,并且都可以进行下标操作。C语言把数组下标改写成指针偏移量的根本原因是指针和偏移量是底层硬件所使用的基本模型。在函数参数情况下,编译器必须把数组形式改写成指向数组第一个元素的指针形式。编译器只向函数传递数组的地址,而不是整个数组的拷贝。(《深入理解计算机系统》第 3 章 程序的机器级表示,在这方面讲解的比较详细。)


数组与指针的其他区别

指针 数组
保存数据的地址 保存数据
间接访问数组,首先取得指针的内容,把它作为地址,然后从这个地址提取数据。如果指针有一个下标[I],就把指针的内容加上 I 作为地址,从中提取数据 直接访问数据,a[I]只是简单地以 a+I 为地址取得数据
通常用于动态数据结构 通常用于存储固定数目且数据类型相同的元素
相关的函数 malloc(),free() 隐式分配和删除
通常指向匿名数据 自身即为数据名

作者: Petrus.Z

Created: 2021-09-01 Wed 00:38