每次写C函数时,我一般会在其函数名前加上 extern
, 或许有人抱有疑问了:C函数本来不就默认处于全局作用域的吗?加不加 extern
都一样。不过如果要限于文件作用域内,倒是该加上 static
.
说得好,可是这语法是相当冗余的,即定义一个全局函数有两种形式,一是直接声明二是加上 extern
前缀。如果规则这么简单倒还好,但由于 C/C++ 糟糕的编译模型,我们不得不处理另一种问题: 如何声明一个全局函数?
其实规则应该类似:直接声明一个函数,或在函数的声明加上 extern
前缀, 不定义函数体 。
然而这意味着代码复杂度两两相乘,即可以有四种情况:
extern
, 函数声明没有 extern
. extern
, 函数声明没有 extern
. extern
, 函数声明有 extern
. extern
, 函数声明有 extern
. 更何况,您如何确定某文件是不是只有某函数的声明,即函数定义在别的文件。可万一该文件后文还有函数定义呢?再来个 static
语法规则, 记忆负担有您受的。
其实要解决该问题好办,一开始就只记住 extern
和 static
关键字,且它们分别对应于全局作用域函数和文件作用域函数。若用全局作用域函数,文件要么有 extern 定义,要么有 extern 声明,且有只有一个文件包含 extern 定义,其他文件只能可以有一个 extern 声明;若用文件作用域函数,则在一个文件写 static 定义即可。 禁止用没有 extern 或 static 关键字的函数 ,为了减轻记忆负担,关键字多多益善。
可是,C新手一开始接触到的函数往往是不加 extern
的,如果入门书写的不好,比如依次教「声明与定义」, static
, 最后才教 extern
. 那新手就很容易被弄得晕头转向,尤其入门书没有教好编译模型的话。事实上 K&R 之所以不适合入门,原因之一就是这个,我记得它没教编译模型,我后来读 Unix 编程环境时,花了好大费劲才弄懂这些。
其实上文关系到了 C/C++ 一个重大的特征之一: 您有时不得通过种种关键字,来保证程序行为预期。 此外,善用关键字,可以大大提高可读性。
我给这现象起了名,就叫「关键字之戒」, 所有合格的 C/C++ 开发者都绕不过这严苛的戒律修行 。掐指一算,我举一些对应的编程规范吧,若无特别说明,一般针对 C++:
const
, 比如复制构造的参数。否则就传普通(引用)形参,比如 const
. constexpr
. default
或 delete
. explicit
, 以免意外的隐式转换构造。 public
或 private
, 哪怕您知道它默认用 private
. virtual
. void
关键字, 毕竟空形参则意味着形参不定 。 final
. 更多规范参见:
不过话说回来,关键字之戒可能恰恰反映了 C/C++ 作为系统编程语言,对底层程序入木三分的控制吧,与复杂度一样名副其实的细度。当然,新一代的系统编程语言应该可以通过更好的语法设计,大破弃键字之戒。比如 C++ 相对于 C, 废除了空形参意味着形参不确定的特性,引入了代替 extern
和 static
的命名空间,C++17 更有传说中的模块编译模型。Rust 可能更青出于蓝。
Written with StackEdit .