C 和 C++ 之间的连接差异?

发布于 2024-08-17 01:35:59 字数 446 浏览 4 评论 0原文

我已经阅读了关于外部/内部链接的现有问题。我的问题是不同的 - 如果我在 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++ 有什么区别?此行为是否未定义且依赖于编译器?这段代码有多“糟糕”?如果我想重构它我该怎么办(我遇到过很多这样写的旧代码)?

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

爺獨霸怡葒院 2024-08-24 01:35:59

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

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

int a;

在 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.

int a;

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.

半寸时光 2024-08-24 01:35:59

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

第一个 C++ 编译器是
作为 C 源代码的转换器实现
代码,然后将被编译
目标代码的 C 编译器;因为
其中,符号名称必须符合
C 标识符规则。甚至后来,
随着编译器的出现
生成的机器代码或汇编
直接,系统的链接器
一般不支持C++符号,
并且仍然需要重整。

关于兼容性

为了给编译器厂商
更大的自由度,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 的函数,其第一个参数是整数,第二个参数是指向 double 的指针。


This is caused by C++'s name mangling. From Wikipedia:

The first C++ compilers were
implemented as translators to C source
code, which would then be compiled by
a C compiler to object code; because
of this, symbol names had to conform
to C identifier rules. Even later,
with the emergence of compilers which
produced machine code or assembly
directly, the system's linker
generally did not support C++ symbols,
and mangling was still required.

With regards to compatibility:

In order to give compiler vendors
greater freedom, the C++ standards
committee decided not to dictate the
implementation of name mangling,
exception handling, and other
implementation-specific features. The
downside of this decision is that
object code produced by different
compilers is expected to be
incompatible. There are, however,
third party standards for particular
machines or operating systems which
attempt to standardize compilers on
those platforms (for example C++
ABI[18]); some compilers adopt a
secondary standard for these items.

From
http://www.cs.indiana.edu/~welu/notes/node36.html
the following example is given:


For example for the below C code

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

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

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

Its symbol table would be (by dump -t)

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

For same file, if compile in g++, then the symbol table would be

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

_Z3bariPd means a function whose name is bar and whose first arg is integer and second argument is pointer to double.


痴梦一场 2024-08-24 01:35:59

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.

自找没趣 2024-08-24 01:35:59

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.

蘑菇王子 2024-08-24 01:35:59

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

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

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

如果您确实想破坏安全机制,可以像这样禁用重整:

extern "C" struct abc foo;

… other file …

extern "C" struct foo foo;

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

You have found the One Definition Rule. Clearly your program has a bug, since

  • There can only be one object named foo once the program is linked.
  • If some source file includes all the header files, it will see two definitions of 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:

extern "C" struct abc foo;

… other file …

extern "C" struct foo foo;

extern "C" instructs the linker to use C ABI conventions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文