C和C ++之间的联系有何不同?

时间:2010-01-08 08:08:38

标签: c++ c name-mangling

我已经在SO上阅读了有关外部/内部联系的现有问题。我的问题不同 - 如果我在CC++下的不同翻译单元中对同一变量和外部链接有多个定义,会发生什么?

例如:

/*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 ++有什么区别?此行为是否未定义且依赖于编译器?这个代码有多“糟糕”,如果我想重构它,我该怎么办(我遇到过很多像这样编写的旧代码)?

5 个答案:

答案 0 :(得分:4)

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

文件范围内的以下声明之间存在语言差异,但它并不直接与您的示例有关。

int a;

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

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

答案 1 :(得分:2)

这是由C ++的名称错误造成的。来自Wikipedia

  

第一批C ++编译器是   作为C源的翻译实现   代码,然后由编译   一个C编译器来对象代码;因为   其中,符号名称必须符合   到C标识符规则。甚至以后   随着编译器的出现   生产机器代码或组装   直接,系统的链接器   一般不支持C ++符号,   还需要修剪。

关于compatibility

  

为了给编译器供应商   更大的自由,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;
}

其符号表将是(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且第一个arg为整数的函数,第二个参数是指向double的指针。


答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

您找到了One Definition Rule。很明显,你的程序有一个错误,因为

  • 程序链接后,只能有一个名为foo的对象。
  • 如果某个源文件包含所有头文件,则会看到foo
  • 的两个定义

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

如果您真的想要破坏安全机制,可以禁用这样的修改:

extern "C" struct abc foo;

......其他档案......

extern "C" struct foo foo;

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