5

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

 3 years ago
source link: https://blog.popkx.com/c%E8%AF%AD%E8%A8%80%E5%9F%BA%E6%9C%AC%E5%8A%9F%E4%BF%AE%E7%82%BC%E7%A7%98%E7%B1%8D%E7%AC%AC4%E8%8A%82-%E4%B8%BA%E4%BB%80%E4%B9%88%E6%9C%89%E4%BA%BA%E8%AF%B4c%E8%AF%AD%E8%A8%80/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

有读者问我:在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";

在内存中可以对应下面这张图,请看:
a373f78b6e462658fe7681e25b35e783.png
显然,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语言程序是如何传递和解释数据的就可以了,毕竟这才是一切的最终落脚点。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK