Kernighan和Ritchie合写的《The C Programming Language, Second Edition》虽然是二三十年前的书了, 但仍被认为是有关C语言的经典之作。被人饱含深情的称之为“K&R”。
题目略有些标题党,但是在C范围内,称之为错,也勉强说得过去。
01
—
清晰的图和令人困惑表述
在K&R第二版第五章的9和10节中有这样两个图。
俗话所,一图胜千言。两个图对于理解name和argv是大有用处的:图1中是说name是一个数组,数组的每个元素是指向字符串的指针;图2是说argv是一个指针,它指向一个数组,这个被指向的数组的元素是指向字符串的指针。
单从这两个图上看,name和argv的类型是不同的,但从C/C++中带参数的main的参数列表写法:main(int argc, char *argv[])可以看到,name和argv的声明方式是一样的。从语法上讲,这样的声明方式是在告诉编译器,name和argv都是数组,就像图1那样。那么为什么K&R会说argv是一个指针呢?
在第10节中,K&R给出的第二版本的echo函数是这样写的:
如果argv是数组的话,那么在编译时应该会出错,但是实际上这种写法可以可以正常编译运行。
这是我称之为“错”的地方:为什么明明声明了一个数组,但是却能够当做指针来用呢?
02
—
K&R的解释没错
K&R说argv是指针是没有问题的,只是需要加一些注解。当然,要提前说明,这部分只是笔者的理解,可能是有瑕疵的,也可能是不靠谱的。
K&R第五章的标题是“指针和数组”,这章的开篇,K&R就写到“指针和数组是紧密相关的。”但紧密相关和完全一样毕竟是两码事儿。
比如, 可以用指针打印数组。
但是如果将12行中*pa++改成*a++,将会编译不通过:
原因就是指针是一个变量,而数组名却不是。
那么为什么K&R的第二个版本的echo在编译时不报错呢?
问题的关键在函数调用。
当数组名作为函数参数时,实际传递的这个数组第一个元素的地址,也就意味着实际传递的是指针。
这就是K&R说argv是指针的原因。将带参数的main写成main(int argc, char **argv)也许是一种更好的方式。
通过阅读编译器生成的汇编代码,那么函数调用的过程就可以看得更加清楚明白。
一言以蔽之,标题所说的“错” ,实际上并不是错,在理解了指针和数组的关系后,就能够理解为什么K&R和C++ Primer对于argv的表述虽不尽相同,但本质上一样的。
领取专属 10元无门槛券
私享最新 技术干货