10

我已经阅读了关于外部/内部链接的现有问题。我的问题是不同的 - 如果我在不同的翻译单元中有多个相同变量的定义和外部链接会发生C什么C++

例如:

/*file1.c*/

typedef struct foo {
    int a;
    int b;
    int c;
} foo;

foo xyz;


/*file2.c*/

typedef struct abc {
    double x;
} foo;

foo xyz;

使用Dev-C++,作为C程序,上述程序编译链接完美;而如果将其编译为 C++ 程序,则会出现多重重新定义错误。为什么它应该在 C 下工作,与 C++ 有什么区别?这种行为是否未定义且依赖于编译器?这段代码有多“糟糕”,如果我想重构它应该怎么做(我遇到过很多这样写的旧代码)?

4

5 回答 5

4

C 和 C++ 都有一个“一个定义规则”,即每个对象只能在任何程序中定义一次。违反此规则会导致未定义的行为,这意味着您在编译时可能会或可能不会看到诊断消息。

以下声明在文件范围内存在语言差异,但它与您的示例问题没有直接关系。

int a;

在 C 中,这是一个暂定定义。它可以与同一翻译单元中的其他暂定定义合并以形成单个定义。在 C++ 中,它始终是一个定义(您必须使用extern来声明一个对象而不定义它),并且同一翻译单元中同一对象的任何后续定义都是错误的。

在您的示例中,两个翻译单元的暂定定义都有一个(冲突的)xyz定义。

于 2010-01-08T08:26:07.370 回答
2

这是由 C++ 的名称修改引起的。来自维基百科

第一个 C++ 编译器被实现为 C 源代码的翻译器,然后由 C 编译器编译为目标代码;因此,符号名称必须符合 C 标识符规则。甚至后来,随着直接产生机器码或汇编的编译器的出现,系统的链接器普遍不支持 C++ 符号,仍然需要 mangling。

关于兼容性

为了给编译器供应商更大的自由度,C++ 标准委员会决定不规定名称修饰、异常处理和其他特定于实现的特性的实现。这个决定的缺点是不同编译器生成的目标代码预计是不兼容的。然而,针对特定机器或操作系统的第三方标准试图在这些平台上标准化编译器(例如 C++ ABI[18]);一些编译器对这些项目采用二级标准。

http://www.cs.indiana.edu/~welu/notes/node36.html 给出了以下示例:


例如下面的 C 代码

int foo(double*);
double bar(int, double*);

int foo (double* d) 
{
    return 1;
}

double bar (int i, double* d) 
{
    return 0.9;
}

它的符号表是 (by dump -t)

[4]  0x18        44       2     1   0   0x2 bar
[5]  0x0         24       2     1   0   0x2 foo

对于同一个文件,如果在 g++ 中编译,那么符号表将是

[4]  0x0         24       2     1   0   0x2 _Z3fooPd
[5]  0x18        44       2     1   0   0x2 _Z3bariPd

_Z3bariPd表示一个函数,其名称为 bar,第一个参数是整数,第二个参数是指向双精度的指针。


于 2010-01-08T08:21:05.287 回答
1

C++ 不允许多次定义符号。不确定 C 链接器在做什么,一个好的猜测可能是它只是将两个定义映射到同一个符号上,这当然会导致严重错误。

对于移植,我会尝试将单个 C 文件的内容放入匿名名称空间中,这实质上使符号不同,并且是文件的本地符号,因此它们不会在其他地方与相同的名称发生冲突。

于 2010-01-08T08:19:52.433 回答
0

C 程序允许这样做,并将内存视为联合。它会运行,但可能无法满足您的预期。

C++ 程序(类型更强)正确地检测到问题并要求您修复它。如果您真的想要一个联合,请将其声明为一个。如果您想要两个不同的对象,请限制它们的范围。

于 2010-01-08T08:19:51.967 回答
0

你已经找到了一个定义规则。显然你的程序有一个错误,因为

  • foo一旦程序链接,只能命名一个对象。
  • 如果某个源文件包含所有头文件,它将看到foo.

由于“名称修改”,C++ 编译器可以绕过 #1:链接程序中的变量名称可能与您选择的名称不同。在这种情况下,它不是必需的,但这可能是您的编译器检测到问题的方式。但是,#2 仍然存在,因此您不能这样做。

如果你真的想打败安全机制,你可以像这样禁用 mangling:

extern "C" struct abc foo;

…其他文件…</p>

extern "C" struct foo foo;

extern "C"指示链接器使用 C ABI 约定。

于 2010-01-08T08:30:25.407 回答