「2014-2-8」Reading a blog on the pain points of Global Variables of C language

时间:2023-03-09 04:27:14
「2014-2-8」Reading a blog on the pain points of Global Variables of C language

晚上读到一篇《C 语言全局变量那些事儿》。我先前对链接的理解不深,算是涨了一番姿势。此文吐槽的重点,是「非 static 限定的全局变量」带来的看似出人意料(实则可以被合理解释)的行为。虽说都是 tricky 的实验代码,现实环境下,可以通过更好的编程习惯、更有效的 code review 流程和静态检查工具来避免(起码对于「拥有全局作用域的符号被多重定义」这种行为,静态检查肯定是能够吼住的),但了解一下这方面的坑也好。

1.  前三个例子,是基本链接和静态链接。参考这个评论「编译器向汇编器输出强、弱全局符号,链接器再将 .o 文件和静态库“从左至右”进行符号解析,这可以解释前面三个例子」。

所谓「强符号」和「弱符号」的概念:「前者指的是定义并且初始化了的变量,后者指的是未定义或者定义但未初始化的变量」。GNU 链接器对此的决议(resolve)规则是:
#  不允许出现多个相同强符号。
#  如果有一个强符号和多个弱符号,则选择强符号。
#  如果有多个弱符号,那么先决议到 sizeof (type) 最大的那个,如果同样大小,则按照链接顺序选择第一个。

2.  第四个例子,是一个动态链接的例子。参考 1 2 这两则评论。

简单来讲,对于 dll 动态加载符号表的情况,「当一个符号需要被加入全局符号表时,如果相同的符号名已经存在,则后加入的符号被忽略」。《Expert C Programming》里提到的 interpositioning 问题应该也是此来由:某古老的 SunOS 版本,一个非 static 限定的函数 funcTemp 和系统上的内置库函数重名,形参也一致;另一个内置库函数 libDoSomething 调用 funcTemp,结果根据链接顺序和决议规则,libDoSomething 调用的都是用户编写的 funcTemp 函数、而非期望的系统内置 funcTemp 函数,从而出现偶发错误。

P.S. 对于这种隐藏得比较深、又不是必然出错、较难定位的链接问题,C++ 中引入的 name mangling 和 namespace 还是有功的。