2.9 关于开发语言的标准和实现——对不起,前面的内容都是忽悠的

直到这里,都是在我的环境中实际地运行程序,然后结合输出结果进行各种各样的说明。

但是,C 标准并不是根据实现方式制定的,而是根据对开发语言的需求来制定的。

比如,如今大多的 PC 和工作站的操作系统都为我们实现了虚拟地址功能,但是对于那些不具备此功能的操作系统,C 语言编写的应用程序同样能跑得很欢。

此外,前面介绍了“C 语言将自动变量分配在栈中”。其实,这一点并没有在标准中做出规定。因此,即使在每次进入函数的时候将自动变量配置到堆中,也不算违反标准。只是这种实现非常慢,没有人会愚蠢到这种地步罢了。

至于 malloc()的实现,在不同的处理环境中也存在很大的差异。除了 brk()这样 UNIX 特有的系统调用之外,最近也出现了这样的分配方式:在分配较大的内存空间时,使用系统调用 mmap(),之后通过 free()将内存返回给操作系统*

* 默认地不一定被关联到那里。

进一步说,第 1 章的内容都是以“指针就是地址”为前提进行说明的。可是关于这一点,在标准中连一个字也没有提到。正如前面摘录的内容一样,标准中只提到“指针类型描述一个对象,该类对象的值提供对该引用类型实体的引用”。也就是说,如果连实体都能够被引用,即使你不使用虚拟地址也不违反标准。

在标准中,&运算符被称为“地址运算符”,这就让人有点糊涂,具体的说明是:

一元&(地址)运算符的结果是指向由其操作数所表示对象或函数的指针。

无论怎样,此运算符返回的是“指针”,而不是“地址”。

通过%pprintf()的输出结果被定义成:

该指针的值将以实现定义的方式转换为一系列可打印的字符。

由于 C 语言经常通过指针的形式让我们可以直接接触到地址,所以人们常常误以为:

  • 如果没有养成经常关注内容状态的习惯,是不适合进行 C 语言编程的

  • C 语言其实就是结构化的汇编

  • C 语言其实是低级语言

但对于开发应用程序的普通程序员,其实完全没有必要去理会指针就是地址这件事。

可能 C 语言确实就是低级语言。也许有人想说:

明明想使用高级语言,只是在不得已用了 C 语言的情况下,还装腔作势刻意地来一句“啥?C 难道不是低级语言吗”,并以此来标榜自己能够使用低级语言编程,这样可以让他人高看自己一眼。

其实不是这样的。你明知处理环境中指针就是地址,却对此选择性失明,并且坚持抱着“如果将 C 当成高级语言,C 就可以像高级语言那样使用”的想法。那么,等待你的将是成群的 bug。

本章也是在强调“指针就是地址”的基础之上,展开各部分内容的。

比起唠唠叨叨地进行那些抽象的说明,具体地将地址表现出来的方式是不是更直观?将自动变量的地址表示出来,你一下子就能看到栈是如何随着函数调用的发生而不断增长的。如果你没有理解这一点,你同样也无法理解递归调用的原理。

此外,在大部分的运行环境中,C 语言不做运行时检查,这已经是无法回避的现实。因此,如果没有在某种程度上掌握内存的使用方式,正常的调试工作就会遇到麻烦。

另外,遇到不明白的地方,应该通过实验来确认。俗话说得好,实践出真知!

对于本章的例程,请大家一定亲手在自己的环境中尝试运行一下。

但是,一旦大家通过实验接受了“指针就是地址”这个观点,就应该对此提高警惕,否则你就会顺手写出一些抽象度和移植度低下的程序。

此外,一旦出现 bug,请带着“指针就是地址”的观点去解决它——这种姿态在解决 bug 上是恰到好处的。