C 和 C++ 之间的连接差异?
我已经阅读了关于外部/内部链接的现有问题。我的问题是不同的 - 如果我在 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++ 有什么区别?此行为是否未定义且依赖于编译器?这段代码有多“糟糕”?如果我想重构它我该怎么办(我遇到过很多这样写的旧代码)?
I have read the existing questions on external/internal linkage over here on SO. My question is different - what happens if I have multiple definitions of the same variable with external linkage in different translation units under C
and C++
?
For example:
/*file1.c*/
typedef struct foo {
int a;
int b;
int c;
} foo;
foo xyz;
/*file2.c*/
typedef struct abc {
double x;
} foo;
foo xyz;
Using Dev-C++ and as a C program, the above program compiles and links perfectly; whereas it gives a multiple redefinition error if the same is compiled as a C++ program. Why should it work under C and what's the difference with C++? Is this behavior undefined and compiler-dependent? How "bad" is this code and what should I do if I want to refactor it (i've come across a lot of old code written like this)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
C 和 C++ 都有“一次定义规则”,即每个对象在任何程序中只能定义一次。违反此规则会导致未定义的行为,这意味着您在编译时可能会或可能不会看到诊断消息。
文件范围内的以下声明之间存在语言差异,但它并不直接涉及您的示例的问题。
在 C 语言中,这是一个暂定的定义。它可以与同一翻译单元中的其他暂定定义合并以形成单个定义。在 C++ 中,它始终是一个定义(您必须使用 extern 来声明对象而不定义它),并且同一翻译单元中同一对象的任何后续定义都是错误的。
在您的示例中,两个翻译单元的暂定定义都有一个(冲突的)
xyz
定义。Both C and C++ have a "one definition rule" which is that each object may only be defined once in any program. Violations of this rule cause undefined behaviour which means that you may or may not see a diagnostic message when compiling.
There is a language difference between the following declarations at file scope, but it does not directly concern the problem with your example.
In C this is a tentative definition. It may be amalgamated with other tentative definitions in the same translation unit to form a single definition. In C++ it is always a definition (you have to use
extern
to declare an object without defining it) and any subsequent definitions of the same object in the same translation unit are an error.In your example both translation units have a (conflicting) definition of
xyz
from their tentative definitions.这是由 C++ 的名称重整引起的。来自维基百科:
关于兼容性:
从
http://www.cs.indiana.edu/~welu/notes/ node36.html
给出以下示例:
例如,对于下面的 C 代码,
其符号表将为(通过
dump -t
)对于同一个文件,如果在 g++ 中编译,则符号表将为
_Z3bariPd
表示名称为 bar 的函数,其第一个参数是整数,第二个参数是指向 double 的指针。This is caused by C++'s name mangling. From Wikipedia:
With regards to compatibility:
From
http://www.cs.indiana.edu/~welu/notes/node36.html
the following example is given:
For example for the below C code
Its symbol table would be (by
dump -t
)For same file, if compile in g++, then the symbol table would be
_Z3bariPd
means a function whose name is bar and whose first arg is integer and second argument is pointer to double.C++ 不允许对一个符号进行多次定义。不确定 C 链接器在做什么,一个好的猜测可能是它只是将两个定义映射到同一个符号,这当然会导致严重的错误。
为了移植,我会尝试将各个 C 文件的内容放入匿名命名空间中,这本质上使符号不同,并且对于文件来说是本地的,因此它们不会与其他地方的相同名称发生冲突。
C++ does not allow a symbol to be defined more than once. Not sure what the C linker is doing, a good guess might be that it simply maps both definitions onto the same symbol, which would of course cause severe errors.
For porting I would try to put the contents of individual C-files into anonymous namespaces, which essentially makes the symbols different, and local to the file, so they don't clash with the same name elsewhere.
C 程序允许这样做,并将内存视为一个联合。它会运行,但可能不会给您带来预期的结果。
C++ 程序(更强的类型)会正确检测到问题并要求您修复它。如果你真的想要一个工会,就宣布它为一个工会。如果您想要两个不同的对象,请限制它们的范围。
The C program permits this and treats the memory a little like a union. It will run, but may not give you what you expected.
The C++ program (which is stronger typed) correctly detects the problem and asks you to fix it. If you really want a union, declare it as one. If you want two distinct objects, limit their scope.
您已找到单一定义规则。显然你的程序有一个错误,因为
foo
的对象。foo
的两个定义。C++ 编译器可以绕过#1,因为“名称修改”:链接程序中的变量名称可能与您选择的名称不同。在这种情况下,这不是必需的,但这可能是您的编译器检测问题的方式。但 #2 仍然存在,所以你不能这样做。
如果您确实想破坏安全机制,可以像这样禁用重整:
… other file …
extern "C"
指示链接器使用 C ABI 约定。You have found the One Definition Rule. Clearly your program has a bug, since
foo
once the program is linked.foo
.C++ compilers can get around #1 because of "name mangling": the name of your variable in the linked program may be different from the one you chose. In this case, it isn't required, but it's probably how your compiler detected the problem. #2, though, remains, so you can't do that.
If you really want to defeat the safety mechanism, you can disable mangling like this:
… other file …
extern "C"
instructs the linker to use C ABI conventions.