完全将extern "C"放入 c + + 代码是什么?

例如︰

extern "C" {
   void foo();
}
2009-06-25 02:10:07
问题评论:

我希望本文为您介绍︰ http://www.agner.org/optimize/calling_conventions.pdf ,它会告诉您更多有关调用约定和编译器之间的差异。

在我的头顶部的 @Litherum,它告诉编译器将假设您具有交叉编译器编译代码使用 C,该范围。同时,它表明您已经从中获得了foo()函数的 Cpp 文件。

回答:

extern"C"使一个函数名称 c + + 中有 C 链接 (编译器不混乱名称),以便客户端 C 代码可以链接到 (即使用) 您使用 C 兼容的头文件包含只是您的函数声明的函数。函数定义包含以二进制格式 (c + + 编译器编译),然后会将客户端 C 链接链接到使用 C 的名称。

C + + 的重载的函数名称和 C 却没有,因为 c + + 编译器不能只需使用函数名称作为一个唯一的 id 链接到,因此它 mangles 名称通过添加参数的有关信息。C 编译器不需要重整名称,因为您可以重载的 C 中的函数名当您声明一个函数在 c + + 包含 extern"C"链接时,c + + 编译器不会添加到用于链接的名称/参数的类型信息。

您知道,可以显式指定每个单独的声明/定义为"C"链接,或者使用一块要有某些链接声明/定义的序列进行分组︰

extern "C" void foo(int);
extern "C"
{
   void g(char);
   int i;
}

如果您关心技术细节,它们被列入部分 7.5 C + + 03 标准,以下是摘要 (使用 extern"C"的强调)︰

  • extern"C"是链接规范
  • 每个编译器是需要来提供"C"链接
  • 链接规范应只发生在命名空间范围
  • 所有的函数类型、 函数名和变量名有语言链接看到比尔的备注︰ 只有函数名和变量名用外部链接有语言链接
  • 具有不同语言联系的两个函数类型是不同的类型,甚至如果其余部分都相同
  • 链接规范是嵌套的内部一确定最后的链接
  • extern"C"类成员将被忽略
  • 最具有特定名称的一个函数可以具有 (无论命名空间) 的"C"链接
  • extern"C"强制有 (不能使其静态) 的外部链接的函数看到比尔注释︰在外部"C"内的静态是有效的;因此声明实体具有内部链接,并且因此没有语言链接
  • 从 c + + 链接到其他语言中定义的对象和其他语言中定义的 c + + 对象是实现定义与语言的关系。只有在两种语言中实现的对象布局策略非常相似,以至于可以此类链接来实现

C 编译器不使用错位的 c + + 的不会。因此,如果您想从 c + + 程序中调用 c 接口,您必须明确声明,"extern c"作为 c 接口。

@Faisal︰ 不要尝试链接用不同的 c + + 编译器生成的代码,即使所有的外部"C"交叉引用。通常有两种布局的类或用来处理异常或用来确保变量被初始化之前使用或其他此类差异的机制的机制之间的差异,再加上可能需要两个独立 c + + 运行时支持库 (一个用于每个编译器)。

@Leffler-谢谢你们,您使好点。我不打算鼓励使用不同的 c + + 编译器使用 extern"C"。而我所希望提出建议,如果不编写需要链接到其他 c + + 编译器的某些内容,您可能不需要 extern"C"。

外部"C"强制有 (不能使其静态) 的外部链接的函数是不正确的。在外部"C"内的静态是有效的;因此声明实体具有内部链接,并且因此没有语言链接。

所有的函数类型、 函数名和变量名有语言链接也是不正确的。只有函数名和变量名用外部链接具有语言链接。

只是想添加少量信息,因为我从没见过它尚未公布。

经常会看到在 C 标头中的代码如下所示︰

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

它能完成是因为将定义宏"__cplusplus"允许您使用 c + + 代码中,使用的 C 头文件。但可以仍然使用传统 C 代码中,该宏不与之定义,因此它将不会看到唯一 c + + 构造。

虽然我有也观看过 c + + 代码类似︰

extern "C" {
#include "legacy_C_header.h"
}

我想象的完成得同样的事情。

不确定采取哪种方式会更好,但我看过两个。

没有明显的差别。在的情况下的前者,如果编译此文件与普通 gcc 编译器它将生成一个对象,该对象的函数名不出错的地方。如果您然后将 C 和 c + + 对象链接链接器与它将找不到函数。您将需要包括使用 extern 关键字,如第二个代码块中所示的"旧式头"文件。

@Anne: c + + 编译器将查找 unmangled 名也,因为它实现了标头中的extern "C" )。它非常好用,多次使用这种技术。

@Ben。我指句子Not sure which way is better, but I have seen both.没有区别 !如果在编译时使用gcc的第一个文件,它不会遇到extern "C"短语...因此,如果您不使用 c + + 代码中的第二个短语你仍在困难中。如果同一个编译器用于所有文件,没有任何区别。

@Anne︰ 不是这样的第一个也是很好。它由 C 编译器忽略和 c + + 中有第二个相同的效果。编译器无法遇到extern "C"之前还是之后它包括标头不太关心。编译器到达时,仍是文本的只是文本的一个长流的预处理过。

@Anne,不对,我认为您已经受到源中的其他一些错误因为您描述有误。没有任何版本的g++弄错了,对于任何目标,在过去的 17 年中任何时候至少。第一个示例中的整个点是︰ 不管是否使用 C 或 c + + 编译器,没有名称重整将完成extern "C"块中的名称。

在每个 c + + 程序中,所有非静态函数的二进制文件中表示为符号。这些符号是特殊文本字符串,用于唯一标识该程序中的函数。

在 C 中,符号名称是该函数的名称相同。这是可能的因为在 C 中任何两个非静态函数可以不具有相同的名称。

因为 c + + 允许重载,并且具有许多功能,C 却没有 — — 类、 成员函数、 异常规范的它不可能只为符号名,使用函数名。若要解决这个问题,c + + 使用所谓名称重整,其中转换函数名以及所有必需的信息 (如数字和参数大小) 到只有编译器知道一些怪怪的字符串。

如果指定为 extern C 函数时,编译器不会执行名称重整与之,它可以直接访问使用的符号名称。

这是用于调用此类函数中使用dlsym()dlopen()时方便。

准确放置外部"C"到 c + + 代码是什么?

让我们反编译对象文件 g + + 生成可以看到什么内容列入在此实现。

生成示例

输入︰

void f() {}
void g();

extern "C" {
    void ef() {}
    void eg();
}

/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }

使用 GCC 4.8 Linux ELF 输出进行编译︰

g++ -c a.cpp

反编译符号表︰

readelf -s a.o

输出结果包含︰

Num:    Value          Size Type    Bind   Vis      Ndx Name
  8: 0000000000000000     6 FUNC    GLOBAL DEFAULT    1 _Z1fv
  9: 0000000000000006     6 FUNC    GLOBAL DEFAULT    1 ef
 10: 000000000000000c    16 FUNC    GLOBAL DEFAULT    1 _Z1hv
 11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv
 12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND eg

解释

我们看到︰

  • efeg存储在符号具有相同的名称,如下所示的代码

  • 其他符号已出错。让我们 unmangle 它们︰

    $ c++filt _Z1fv
    f()
    $ c++filt _Z1hv
    h()
    $ c++filt _Z1gv
    g()
    

结论︰ 两个以下的符号类型都出错︰

  • 定义
  • 声明但未定义的 (Ndx = UND),以从另一个对象文件链接或运行时提供

因此,调用时,您将需要extern "C"这两个︰

  • 从 c + + C︰ 告诉g++期望产生的gcc的 unmangled 的符号
  • C︰ 从 c + + 告诉g++生成的gcc使用的 unmangled 的符号

在外部 C 中不起作用的事情

它很明显需要名称重整任何 c + + 功能将不 wok 内extern C:

extern "C" {
    // Overloading.
    // error: declaration of C function ‘void f(int)’ conflicts with
    void f();
    void f(int i);

    // Templates.
    // error: template with C linkage
    template <class C> void f(C i) { }
}

没有任何 C 头将编译使用 extern"C"。C 标头中的标识符与 c + + 关键字 c + + 编译器发生的冲突时就会对此抱怨。

例如,我见过下面的代码在 g + + 失败︰

extern "C" {
struct method {
    int virtual;
};
}

比较有意义,但要移植到 c + + 的 C 代码时,请记住的东西。

extern "C"表示使用 C 链接,如下所述其他答案。这并不表示将"编译为 C 的内容"或任何东西。int virtual;是无效的 c + + 并指定不同的链接不会改变的。

请输入您的翻译

In C++ source, what is the effect of extern “C”?

确认取消