C语言基本功修炼秘籍第4节,为什么有人说“C语言数组和指针是等价的”?

有读者问我:在C语言代码源文件里定义 char a[6],然后又在别的源文件里做了如下声明 extern char * a,为什么 extern 的 a 无法正常使用呢?

这是当然的了。在一个C语言代码源文件里定义字符数组,又在别的源文件里声明字符指针,显然是两码事。extern char * a 并不是声明数组,所以无法和预期的实际定义 char a[6] 匹配,自然不可以正常使用。正确的做法可以是 extern char a[]。

读者仍然有疑惑,“但是我听说 char a[] 和 char * a 是等价的啊。”

其实并不等价,估计这位读者听说的“等价”概念与C语言函数的形参有关。即使某些时候,数组和指针的特性看起来很相似,用起来也大同小异,但是数组怎么可能和指针是等价的呢

在C语言程序中,char a[6] 这种定义方式是向系统申请 6 个字节的内存空间,并且使用符号 'a' 管理它,也即符号 a 所在位置可以存放 6 个字节的数据。而 char* p 这种定义方式则是申请了一个变量空间,该变量空间存放一个名字为“p”的指针。指针 p 可以指向任意地址。

例如,下面这两行C语言代码:

char a[] = "hello";
char *p = "world";

在内存中可以对应下面这张图,请看:

显然,C语言程序中的数组和指针是有所区别的,它们并不完全等价。

客观看待C语言中“指针和数组等价”这句话

从上面的实例可以看出,这句话并不严谨,甚至是错误的。C语言初学者其实误解了这句话的真正含义,如果非要说数组和指针是“等价的”,那也不是说数组和指针完全一样,可以相互替换,而是说数组和指针在使用上有些相似。事实上,指针可以相当方便的访问数组,甚至被当作数组一样使用。

例如下面这段C语言代码:

char a[] = "hello world";
char *p = a;

p[0] = 'H'; /** 像数组一样使用指针 */
char c = p[3];  /** 像数组一样读 */

在C语言程序中,当某个表达式语句中出现数组时,编译器都会隐式的生成指向数组第一个元素的指针,类似于程序员显式的指定 &a[0] 一样。

当然,数组名作为操作数时除外,例如 &a,sizeof(a) 等。

所以虽然严格来说,C语言中的指针和数组是两个概念,但是它俩的关系确实又比较暧昧。编译器在处理数组 a[i] 时,数组名 a 常常会退化成指针。如果将 a 赋值给指针 p:p = a,那么 p[i] 和 a[i] 其实是同一块内存区域。

正是因为C语言程序中的数组和指针有如此“暧昧”的关系,函数的参数才完全可以使用指针形参,接收数组实参。

C语言函数的指针和数组参数

由于C语言程序中的数组常会退化成指针,所以数组其实从来都不会传递给函数。但是为了便于理解,读者依然可以假装函数接收到了数组作为参数,例如下面这段C语言代码:

void f(char a[])
{
    ... 
}

从字面上来看,上面这段C语言代码对函数 f 的声明其实是没有用的,编译器会反过来假装你写了一个指针参数的函数,相关C语言代码如下:

void f(char *a)
{
...
}

如果函数 f 内部在使用 a 时,将其当做普通的数组来使用,或者 f 本身就是用于处理数组的函数,那么称函数 f “接收数组做参数”也并没有什么大错。

事实上,下面这段简短的C语言代码可以说明函数 f 实际上接收的并不是数组:

char a[10];

void f(char a[10])
{
    int size = sizeof(a);
    ...
}

f(a);

上述C语言代码函数 f() 中的 size 并不会等于 10,而是等于指针的宽度(在大多64位机器上等于8,在大多数32机器上等于4),这其实从侧面说明了即使在定义函数时,写成 f(char a[10]) 的数组参数形式,并且传递给它的的确是一个数组,编译器在函数内部也不会把 a 当作一个真正的数组处理,所以实际上,函数 f() 接收的仍然是一个指针。

小结

本节主要讨论了C语言程序中指针与数组的关系,可见“数组和指针是等价的”这句话并不准确。但是不可否认的是,C语言中数组和指针有时又的确关系“暧昧”,这一点从本文后面的两个例子可以看出。初学者看到这里可能会感到C语言扑朔迷离,但其实不用纠结“数组和指针是等价的”究竟对不对,只要明白C语言程序是如何传递和解释数据的就可以了,毕竟这才是一切的最终落脚点。

阅读更多:   C语言
添加新评论

icon_redface.gificon_idea.gificon_cool.gif2016kuk.gificon_mrgreen.gif2016shuai.gif2016tp.gif2016db.gif2016ch.gificon_razz.gif2016zj.gificon_sad.gificon_cry.gif2016zhh.gificon_question.gif2016jk.gif2016bs.gificon_lol.gif2016qiao.gificon_surprised.gif2016fendou.gif2016ll.gif