1

我有一种情况,使用与此类似的 C++ 编译器构建 C 代码库:

库文件

extern int const values[2] = {1, 2};

库文件

#include "lib.h"

主程序

#include <iostream>

extern int const values[2];

int main() {
    std::cout << values[0] << ":" << values[1] << std::endl;
}

由于C++03 Standard Annex C Compatibility C.1.2 Clause 3中指出的内容,我不得不添加 extern 。 (用编译-fpermissive将把它扫到地毯下。)

顺便说一句,它values在 objdump 中的显示方式在 objdump 之前是这样的extern

$ objdump -t lib.o | grep values
0000000000000000 l     O .rodata    0000000000000008 _ZL6values

$ objdump -t main.o | grep values
0000000000000000         *UND*  0000000000000000 values

...然后添加后是这样的:

$ objdump -t lib.o | grep values
0000000000000000 g     O .rodata    0000000000000008 values

$ objdump -t main.o | grep values
0000000000000000         *UND*  0000000000000000 values

所以名称 mangling 被删除了,我们看到“L”变成了“G”,并且链接器不会抱怨values未定义。


现在想象两个非常相似的文件的相同情况,以相同的方式修改:

tmp-exttypes.h

extern const REBYTE Reb_To_RXT[REB_MAX] = { /* bunch of stuff */ };

a-lib.c

extern const REBYTE Reb_To_RXT[REB_MAX];

这些是项目中仅有的两个 Reb_To_RXT 定义,构建干净。但它没有链接,当我 objdump 提到它的唯一两个文件时,我得到:

$ objdump -t a-lib.o | grep Reb_To_RXT
00000000         *UND*  00000000 Reb_To_RXT

$ objdump -t f-extension.o | grep Reb_To_RXT
00000080 l     O .rodata    00000038 _ZL10Reb_To_RXT

它说 L,它的名字被破坏了。这并没有让这个简单得多的例子变得快乐。但是我想知道每次出现时都带有extern怎么会发生这种情况。我是否正确地相信这是一支确凿的证据……而且通常不应该发生仅声明为 extern 的东西在任何地方都不应该具有本地联系?

4

2 回答 2

3

我不明白你什么。

但 …

extern int const values[2] = {1, 2};

在头文件中,如果该头包含在多个翻译单元中,则您有 UB。很可能但不一定会出现链接错误。

一种解决方案:在标头中声明数组,例如

extern int const values[2];

但在实现文件中定义它(使用初始化程序)。

另一种解决方案是使用模板技巧或内联函数技巧在头文件中定义数组。

内联函数技巧:

typedef int const Values[2];

inline Values& valuesRef()
{
    static Values   theValues = {1, 2};
    return theValues;
}

static Values& values = valuesRef();
于 2012-12-17T05:29:58.763 回答
1

是,对的。

你(我)正在编辑的文件是自动生成的,实际上是由一个干净的 make 销毁的,在写下思考过程的过程中发现了这一点。您在该特定标题中看到的“tmp”应该暗示“临时,不要编辑”,如果文件没有在您正在使用的编辑器中重新加载,它可能会令人困惑。

(无论如何,完成了机构知识的思路。:-P)

于 2012-12-17T04:44:03.023 回答