extern“C”有什么作用? 在 C++ 中?
将 extern "C"
放入 C++ 代码中到底有什么作用?
例如:
extern "C" {
void foo();
}
What exactly does putting extern "C"
into C++ code do?
For example:
extern "C" {
void foo();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(18)
extern "C"
使 C++ 中的函数名称具有 C 链接(编译器不会破坏名称),以便客户端 C 代码可以使用包含以下内容的 C 兼容头文件链接到(使用)您的函数只是你的函数的声明。 您的函数定义包含在二进制格式中(由 C++ 编译器编译),然后客户端 C 链接器将使用 C 名称链接到该二进制格式。由于 C++ 具有函数名称重载,而 C 没有,因此 C++ 编译器不能仅使用函数名称作为链接到的唯一 id,因此它会通过添加有关参数的信息来破坏名称。 C 编译器不需要修改名称,因为您不能重载 C 中的函数名称。当您在 C++ 中声明函数具有
extern "C"
链接时,C++ 编译器不会添加参数/参数在用于链接的名称中键入信息。正如您所知,您可以显式指定每个单独的声明/定义的
extern "C"
链接,或者使用块对一系列声明/定义进行分组以具有特定的链接:如果您关心技术细节,它们列在 C++03 标准的第 7.5 节中,这里是一个简短的总结(重点是
extern "C"
):extern "C"
是链接规范所有函数类型、函数名称和变量名称都具有语言链接查看理查德的评论: 只有具有外部链接的函数名称和变量名称才具有语言链接extern "C"<对于类成员,忽略 /code>
参见 Richard 的评论:extern "C"
强制函数具有外部链接(不能使其静态)extern "C"
内的static
有效; 如此声明的实体具有内部链接,因此不具有语言链接extern "C"
makes a function-name in C++ have C linkage (compiler does not mangle the name) so that client C code can link to (use) your function using a C compatible header file that contains just the declaration of your function. Your function definition is contained in a binary format (that was compiled by your C++ compiler) that the client C linker will then link to using the C name.Since C++ has overloading of function names and C does not, the C++ compiler cannot just use the function name as a unique id to link to, so it mangles the name by adding information about the arguments. A C compiler does not need to mangle the name since you can not overload function names in C. When you state that a function has
extern "C"
linkage in C++, the C++ compiler does not add argument/parameter type information to the name used for linkage.Just so you know, you can specify
extern "C"
linkage to each individual declaration/definition explicitly or use a block to group a sequence of declarations/definitions to have a certain linkage:If you care about the technicalities, they are listed in section 7.5 of the C++03 standard, here is a brief summary (with emphasis on
extern "C"
):extern "C"
is a linkage-specificationAll function types, function names and variable names have a language linkageSee Richard's Comment: Only function names and variable names with external linkage have a language linkageextern "C"
is ignored for class membersSee Richard's comment:extern "C"
forces a function to have external linkage (cannot make it static)static
insideextern "C"
is valid; an entity so declared has internal linkage, and so does not have a language linkage只是想添加一些信息,因为我还没有看到它发布。
您经常会在 C 头文件中看到如下代码:
这样做的目的是允许您将 C 头文件与 C++ 代码一起使用,因为将定义宏
__cplusplus
。 但您也仍然可以将其与旧版 C 代码一起使用,其中宏未定义,因此它不会看到唯一的 C++ 构造。尽管如此,我也见过 C++ 代码,例如:
我想它可以完成同样的事情。
不确定哪种方式更好,但两种方式我都见过。
Just wanted to add a bit of info, since I haven't seen it posted yet.
You'll very often see code in C headers like so:
What this accomplishes is that it allows you to use that C header file with your C++ code, because the macro
__cplusplus
will be defined. But you can also still use it with your legacy C code, where the macro is NOT defined, so it won't see the uniquely C++ construct.Although, I have also seen C++ code such as:
which I imagine accomplishes much the same thing.
Not sure which way is better, but I have seen both.
反编译
g++
生成的二进制文件以查看发生了什么main.cpp
编译和反汇编生成的 ELF 输出:
输出包含:
解释
我们看到:
ef
和eg
存储在与代码中同名的符号中其他符号被破坏。 让我们来整理一下它们:
<前><代码>$ c++filt _Z1fv
F()
$ c++filt _Z1hv
H()
$ c++filt _Z1gv
G()
结论:以下两种符号类型都没有被破坏:
Ndx = UND
),将在链接或运行时从另一个提供目标文件因此,在调用以下内容时,您将需要
extern "C"
:g++
期望由gcc
g++
为gcc
生成未损坏的符号以使用在extern C中不起作用的东西
很明显,任何需要的C++功能名称修改在
extern C
中不起作用:C++ 示例中的最小可运行 C
为了完整起见,对于新手来说,另请参阅:如何在 C++ 项目中使用 C 源文件?
调用C++ 中的 C 非常简单:每个 C 函数只有一个可能的非重整符号,因此不需要额外的工作。
main.cpp
c.h
c.c
运行:
如果没有
extern "C"
,链接将失败:因为
g++
期望找到一个损坏的f
,其中>gcc
没有产生。GitHub 上的示例。
从 C 示例中最小化可运行的 C++
从 C 调用 C++ 有点困难:我们必须手动创建我们想要公开的每个函数的非损坏版本。
这里我们说明如何向 C 公开 C++ 函数重载。
main.c
cpp.h
cpp.cpp
运行:
如果没有
extern "C"
,它会失败:因为
g++
生成损坏gcc
找不到的符号。GitHub 上的示例。
当我包含来自 C++ 的 C 标头时,
extern "c"
在哪里?cstdio
)可能依赖于#pragma GCC system_header
其中 https://gcc.gnu.org/ onlinedocs/cpp/System-Headers.html 提到:“在某些目标上,例如 RS/6000 AIX,当编译为 C++ 时,GCC 隐式地用 'extern'C'' 块包围所有系统头。”,但是我没有完全确认。/usr/include/unistd.h
之类的 POSIX 标头位于:我需要一个外部“C”块来包含标准 POSIX C 标头吗? 通过__BEGIN_DECLS
,在 Ubuntu 20.04 上复制。__BEGIN_DECLS
通过#include
包含。在 Ubuntu 18.04 中测试。
Decompile a
g++
generated binary to see what is going onmain.cpp
Compile and disassemble the generated ELF output:
The output contains:
Interpretation
We see that:
ef
andeg
were stored in symbols with the same name as in the codethe other symbols were mangled. Let's unmangle them:
Conclusion: both of the following symbol types were not mangled:
Ndx = UND
), to be provided at link or run time from another object fileSo you will need
extern "C"
both when calling:g++
to expect unmangled symbols produced bygcc
g++
to generate unmangled symbols forgcc
to useThings that do not work in extern C
It becomes obvious that any C++ feature that requires name mangling will not work inside
extern C
:Minimal runnable C from C++ example
For the sake of completeness and for the newbs out there, see also: How to use C source files in a C++ project?
Calling C from C++ is pretty easy: each C function only has one possible non-mangled symbol, so no extra work is required.
main.cpp
c.h
c.c
Run:
Without
extern "C"
the link fails with:because
g++
expects to find a mangledf
, whichgcc
did not produce.Example on GitHub.
Minimal runnable C++ from C example
Calling C++ from C is a bit harder: we have to manually create non-mangled versions of each function we want to expose.
Here we illustrate how to expose C++ function overloads to C.
main.c
cpp.h
cpp.cpp
Run:
Without
extern "C"
it fails with:because
g++
generated mangled symbols whichgcc
cannot find.Example on GitHub.
Where is the
extern "c"
when I include C headers from C++?cstdio
might be relying on#pragma GCC system_header
which https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html mentions: "On some targets, such as RS/6000 AIX, GCC implicitly surrounds all system headers with an 'extern "C"' block when compiling as C++.", but I didn't fully confirm it./usr/include/unistd.h
are covered at: Do I need an extern "C" block to include standard POSIX C headers? via__BEGIN_DECLS
, reproduced on Ubuntu 20.04.__BEGIN_DECLS
is included via#include <features.h>
.Tested in Ubuntu 18.04.
在每个 C++ 程序中,所有非静态函数都在二进制文件中表示为符号。 这些符号是特殊的文本字符串,唯一标识程序中的函数。
在C中,符号名与函数名相同。 这是可能的,因为在 C 中没有两个非静态函数可以具有相同的名称。
由于 C++ 允许重载并且具有许多 C 所没有的功能(例如类、成员函数、异常规范),因此不可能简单地使用函数名称作为符号名称。 为了解决这个问题,C++ 使用所谓的名称修饰,它将函数名称和所有必要的信息(例如参数的数量和大小)转换为一些仅由编译器和链接器处理的奇怪字符串。
因此,如果您将一个函数指定为 extern C,编译器不会对其执行名称修改,并且可以直接
使用其符号名称作为函数名称进行访问。
使用
dlsym()
和dlopen()
调用此类函数时,这很方便。In every C++ program, all non-static functions are represented in the binary file as symbols. These symbols are special text strings that uniquely identify a function in the program.
In C, the symbol name is the same as the function name. This is possible because in C no two non-static functions can have the same name.
Because C++ allows overloading and has many features that C does not — like classes, member functions, exception specifications - it is not possible to simply use the function name as the symbol name. To solve that, C++ uses so-called name mangling, which transforms the function name and all the necessary information (like the number and size of the arguments) into some weird-looking string processed only by the compiler and linker.
So if you specify a function to be extern C, the compiler doesn't performs name mangling with it and it can be directly
accessed using its symbol name as the function name.
This comes handy while using
dlsym()
anddlopen()
for calling such functions.C++ 修改函数名称以从过程语言创建面向对象的语言
大多数编程语言都不是构建在现有编程语言之上的。 C++ 建立在 C 之上,而且它是一种基于过程编程语言构建的面向对象的编程语言,因此存在像
extern "C"
这样的 C++ 表达式,它们提供了向后兼容性C.让我们看下面的例子:
AC 编译器不会编译上面的例子,因为同一个函数
printMe
被定义了两次(即使它们有不同的参数int a
vs字符a
)。但是,C++ 编译器将编译上面的示例。 它并不关心
printMe
被定义了两次。这是因为 C++ 编译器根据函数的参数隐式重命名(mangles)函数。 该语言被设计为面向对象 - 创建具有相同名称的方法(函数)的不同类,并覆盖方法名称(方法重写)基于不同的参数。
extern "C"
所说的是“不要破坏 C 函数名称”尽管 C++ 是基于 C 构建的,但破坏可能会导致 C 代码混乱。 例如,假设我们有一个名为“parent.c”的旧 C 文件,其中
包含
来自不同头文件“parent.h”、“child.h”等的函数名称。如果我们运行“parent.c”通过 C++ 编译器,这将破坏该文件中的函数名称,并且它们将不再与头文件中指定的函数名称匹配。 因此,“parent.h”和“child.h”头文件中的函数名称也需要进行修改。 这对于一些文件来说可能没问题,但如果 C 程序很复杂,重整可能会很慢并导致代码损坏,因此提供一个告诉 C++ 编译器不要重整函数名称的关键字可能会很方便。extern "C"
关键字告诉 C++ 编译器不要破坏(重命名)C 函数名称。例如:
extern "C" void printMe(int a);
C++ mangles function names to create an object-oriented language from a procedural language
Most programming languages aren't built on-top of existing programming languages. C++ is built on-top of C, and furthermore it's an object-oriented programming language built from a procedural programming language, and for that reason there are C++ expressions like
extern "C"
which provide backwards compatibility with C.Let's look at the following example:
A C compiler will not compile the above example, because the same function
printMe
is defined twice (even though they have different parametersint a
vschar a
).However, a C++ compiler will compile the above example. It does not care that
printMe
is defined twice.This is because a C++ compiler implicitly renames (mangles) functions based on their parameters. The language was designed to be object-oriented - to create different classes with methods (functions) of the same name, and to override methods names (method overriding) based on different parameters.
What
extern "C"
says is "don't mangle C function names"Even though C++ was built on C, mangling can cause a mess for C code. For example, imagine we have a legacy C file named "parent.c" that
include
s function names from different header files, "parent.h", "child.h", etc. If we run "parent.c" through a C++ compiler, that will mangle function names in that file, and they will no longer match the function names specified in the header files. So the function names in the "parent.h" and "child.h" header files would need to be mangled as well. This might be okay for a few files, but if the C program is complex, mangling could be slow and cause broken code, so it might be convenient to provide a keyword which tells the C++ compiler not to mangle function names.The
extern "C"
keyword tells a C++ compiler not to mangle (rename) C function names.For example:
extern "C" void printMe(int a);
并不是任何 C 头文件都可以通过仅包装在 extern“C”中而与 C++ 兼容。 当 C 标头中的标识符与 C++ 关键字冲突时,C++ 编译器会对此进行抱怨。
例如,我看到以下代码在 g++ 中失败:
有点道理,但在将 C 代码移植到 C++ 时需要记住这一点。
Not any C-header can be made compatible with C++ by merely wrapping in extern "C". When identifiers in a C-header conflict with C++ keywords the C++ compiler will complain about this.
For example, I have seen the following code fail in a g++ :
Kinda makes sense, but is something to keep in mind when porting C-code to C++.
它以可以从 C 调用该函数的方式更改函数的链接。实际上,这意味着函数名称不是 损坏。
It changes the linkage of a function in such a way that the function is callable from C. In practice that means that the function name is not mangled.
它通知C++编译器在链接时以C风格查找这些函数的名称,因为C和C++编译的函数名称在链接阶段是不同的。
It informs the C++ compiler to look up the names of those functions in a C-style when linking, because the names of functions compiled in C and C++ are different during the linking stage.
extern "C"
旨在被 C++ 编译器识别,并通知编译器所指出的函数是(或将)以 C 风格编译,以便在链接时链接到正确的函数C 函数的版本。extern "C"
is meant to be recognized by a C++ compiler and to notify the compiler that the noted function is (or will be) compiled in C style, so that while linking, it links to the correct version of the function from C.extern "C"
是一个链接规范,用于在Cpp 源文件中调用C 函数。 我们可以调用 C 函数,编写变量,& 包含标题。 函数在 extern 实体 & 中声明 它是在外部定义的。 语法为类型1:
类型2:
例如:
extern "C"
is a linkage specification which is used to call C functions in the Cpp source files. We can call C functions, write Variables, & include headers. Function is declared in extern entity & it is defined outside. Syntax isType 1:
Type 2:
eg:
我之前对 dll(动态链接库)文件使用了 'extern "C"' 来使 main() 等函数“可导出”,以便稍后可以在 dll 的另一个可执行文件中使用它。
也许我曾经使用过它的例子会很有用。
动态
链接库
I used 'extern "C"' before for dll(dynamic link library) files to make etc. main() function "exportable" so it can be used later in another executable from dll.
Maybe an example of where I used to use it can be useful.
DLL
EXE
这个答案是为了不耐烦/有最后期限要满足的人,只有部分/简单的解释如下:
所以
在 C++ 中,名称修饰唯一标识每个函数
在 C 中,即使没有名称修改,每个函数都有唯一标识
要更改 C++ 的行为,即指定特定函数不应该发生名称修改,您可以使用 extern "C " 在函数名称之前,无论出于何种原因,例如从 dll 中导出具有特定名称的函数以供其客户端使用。
阅读其他答案,以获得更详细/更正确的答案。
This answer is for the impatient/ have deadlines to meet to, only a part/simple explanation is below:
So
in C++, with name mangling uniquely identities each function
in C, even without name mangling uniquely identities each function
To change the behaviour of C++, that is, to specify that name mangling should not happen for a particular function, you can use extern "C" before the function name, for whatever reason, like exporting a function with a specific name from a dll, for use by its clients.
Read other answers, for more detailed/more correct answers.
C 编译器编译的函数 void f() 和 C++ 编译器编译的同名函数 void f() 不是同一个函数。 如果您用 C 编写该函数,然后尝试从 C++ 调用它,则链接器将查找 C++ 函数,但找不到 C 函数。
extern "C" 告诉 C++ 编译器您有一个由 C 编译器编译的函数。 一旦你告诉它它是由C编译器编译的,C++编译器就会知道如何正确调用它。
它还允许 C++ 编译器以 C 编译器可以调用它的方式编译 C++ 函数。 该函数正式是 C 函数,但由于它是由 C++ 编译器编译的,因此它可以使用所有 C++ 功能并具有所有 C++ 关键字。
A function void f() compiled by a C compiler and a function with the same name void f() compiled by a C++ compiler are not the same function. If you wrote that function in C, and then you tried to call it from C++, then the linker would look for the C++ function and not find the C function.
extern "C" tells the C++ compiler that you have a function which was compiled by the C compiler. Once you tell it that it was compiled by the C compiler, the C++ compiler will know how to call it correctly.
It also allows the C++ compiler to compile a C++ function in such a way that the C compiler can call it. That function would officially be a C function, but since it is compiled by the C++ compiler, it can use all the C++ features and has all the C++ keywords.
这是带有示例代码的演示。
这段代码显然不能在 C 中编译,因为两个函数共享相同的名称,但可以在 C++ 中编译,这允许函数重载:
在 C++ 中,函数名称不是唯一的符号标识符,C++ 编译器添加“前缀”和“后缀”基于函数原型的符号名称:
但是如果我们希望我们的函数有一个 C 链接,我们必须添加
extern "C"
修饰符现在我们的
int get_message2(char*);
函数有一个旧的 C 链接:This is a demonstration with sample code.
This code clearly wouldn't compile in C because of two functions share same name, but can compile in C++, which allows overloading of functions:
In C++ function names are not unique symbol identifiers, and C++ compiler adds "prefixes" and "suffixes" to symbol name based on function prototype:
But if we want for our function have a C linkage, we must add
extern "C"
modifierNow our
int get_message2(char*);
function has an old C linkage:有趣的是,Visual Studio 和 dumpbin 都没有出现在这个线程中(到目前为止)。 所以我也想添加一些有关此的信息。
(如果有意义,我们也可以将其合并到上述任何答案中。我不确定这里的 SO 哲学。)
使用 Visual Studio 编译时编译器中,每个
extern "C"
符号前面都有一个前导下划线。示例:
在生成的对象上编译并运行
dumpbin /symbols
,符号如下所示:变量相同。
dumpbin /symbols
旁注:对于 C++ 符号,
dumpbin
还会在括号中显示未损坏的符号名称。旁注 2:如您所见,
extern "C"
不会影响类型定义。小心! 如果一个变量在之前某处声明而没有
extern "C"
(例如在头文件中),那么它将使用 C++ 链接进行编译,恕不另行通知:dumpbin /symbols
但是,当函数在之前某处声明时>
extern "C"
,那么 (Microsoft) 编译器会给出明显的错误消息:(讨论了这种差异的原因 此处。)
据我所知,
extern "C"
还告诉编译器使用 C 调用约定。另请参阅 从 DLL 导出 C++ 类 - Eli Bendersky 的网站(Google 仍然找到它,但该网站似乎已经死了):
另请参阅此 常见问题解答:如何混合 C 和 C++< /a>
It is interesting, that neither Visual Studio nor dumpbin occur in this thread (until now). So I want to add some info about this as well.
(If it makes sense, we could merge this into any of the above answers as well. I'm not sure about the SO philosophy here.)
When compiling with Visual Studio compiler, each
extern "C"
symbol is preceded with a leading underscore.example: compiling
and running
dumpbin /symbols
on the resulting object, the symbols looks as following:Same for variables.
dumpbin /symbols
side note: For C++ symbols,
dumpbin
also shows you the unmangled symbol name in parentheses.side note 2: As you can see,
extern "C"
does not affect type definitions.Look out! If a variable is declared somewhere before without
extern "C"
(e.g. in a header file), then it will be compiled with C++ linkage without further notice:dumpbin /symbols
However, when a function is declared somewhere before without
extern "C"
, then the (Microsoft) compiler gives a distinct error message:(The reasons for this difference are discussed here.)
As far as I know,
extern "C"
also tells the compiler to use C calling conventions.see also Exporting C++ classes from a DLL - Eli Bendersky's website (Google still finds it, but the site seems dead):
see also this FAQ: How to mix C and C++
当混合 C 和 C++ 时(即,a. 从 C++ 调用 C 函数;b. 从 C 调用 C++ 函数),C++ 名称重整会导致链接问题。 从技术上讲,只有当被调用函数已使用相应的编译器编译为二进制文件(很可能是 *.a 库文件)时,才会出现此问题。
因此我们需要使用 extern "C" 来禁用 C++ 中的名称修改。
When mixing C and C++ (i.e., a. calling C function from C++; and b. calling C++ function from C), the C++ name mangling causes linking problems. Technically speaking, this issue happens only when the callee functions have been already compiled into binary (most likely, a *.a library file) using the corresponding compiler.
So we need to use extern "C" to disable the name mangling in C++.
在不与其他好的答案冲突的情况下,我将添加一些我的例子。
C++ 编译器到底做了什么:它在编译过程中破坏了名称,因此我们需要告诉编译器专门处理
C
实现。当我们创建 C++ 类并添加
extern "C"
时,我们告诉 C++ 编译器我们正在使用 C 调用约定。原因(我们从 C++ 调用 C 实现):要么我们想从 C++ 调用 C 函数,要么从 C 调用 C++ 函数(C++ 类...等在 C 中不起作用)。
Without conflicting with other good answers, I will add a bit of my example.
What exactly C++ Compiler does: it mangles the names in the compilation process, hence we require telling the compiler to treat
C
implementation specially.When we are making C++ classes and adding
extern "C"
, we're telling our C++ compiler that we are using C calling convention.Reason (we are calling C implementation from C++): either we want to call C function from C++ or calling C++ function from C (C++ classes ... etc do not work in C).
gcc 最近似乎也支持名称修改。 即使在
extern "c"
内部,如果您使用类或重载,它也会自动损坏。我什至使用了很多 cpp 功能。 但代码编译并运行正常。 如果你
nm
,你可以看到main
没有被破坏,但是 myint 被破坏了。gcc seems to support name mangling as well recently. even inside
extern "c"
, if you use class or overloading, it will automatically mangle.I even used many cpp feature. but the code compiles and runs ok. if you
nm
, you can seemain
is not mangled, but myint is.