extern“C”有什么作用? 在 C++ 中?

发布于 2024-07-25 14:43:45 字数 121 浏览 8 评论 0原文

extern "C" 放入 C++ 代码中到底有什么作用?

例如:

extern "C" {
   void foo();
}

What exactly does putting extern "C" into C++ code do?

For example:

extern "C" {
   void foo();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(18

总攻大人 2024-08-01 14:43:46

extern "C" 使 C++ 中的函数名称具有 C 链接(编译器不会破坏名称),以便客户端 C 代码可以使用包含以下内容的 C 兼容头文件链接到(使用)您的函数只是你的函数的声明。 您的函数定义包含在二进制格式中(由 C++ 编译器编译),然后客户端 C 链接器将使用 C 名称链接到该二进制格式。

由于 C++ 具有函数名称重载,而 C 没有,因此 C++ 编译器不能仅使用函数名称作为链接到的唯一 id,因此它会通过添加有关参数的信息来破坏名称。 C 编译器不需要修改名称,因为您不能重载 C 中的函数名称。当您在 C++ 中声明函数具有 extern "C" 链接时,C++ 编译器不会添加参数/参数在用于链接的名称中键入信息。

正如您所知,您可以显式指定每个单独的声明/定义的 extern "C" 链接,或者使用块对一系列声明/定义进行分组以具有特定的链接:

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

如果您关心技术细节,它们列在 C++03 标准的第 7.5 节中,这里是一个简短的总结(重点是 extern "C"):

  • extern "C" 是链接规范
  • 每个编译器都要求提供“C”链接
  • 链接规范仅应出现在命名空间范围内
  • 所有函数类型、函数名称和变量名称都具有语言链接 查看理查德的评论: 只有具有外部链接的函数名称和变量名称才具有语言链接
  • 具有不同语言链接的两个函数类型是不同的类型,即使在其他方面相同的
  • 链接规范嵌套,内部的一个决定最终的链接
  • extern "C"<对于类成员,忽略 /code>
  • 最多一个具有特定名称的函数可以具有“C”链接(无论命名空间如何)
  • extern "C" 强制函数具有外部链接(不能使其静态) 参见 Richard 的评论: extern "C" 内的 static 有效; 如此声明的实体具有内部链接,因此不具有语言链接
  • 从 C++ 到其他语言中定义的对象以及从其他语言到 C++ 中定义的对象的链接是实现定义的且依赖于语言。 只有两种语言实现的对象布局策略足够相似才能实现这种链接

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:

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

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-specification
  • Every compiler is required to provide "C" linkage
  • A linkage specification shall occur only in namespace scope
  • All function types, function names and variable names have a language linkage See Richard's Comment: Only function names and variable names with external linkage have a language linkage
  • Two function types with distinct language linkages are distinct types even if otherwise identical
  • Linkage specs nest, inner one determines the final linkage
  • extern "C" is ignored for class members
  • At most one function with a particular name can have "C" linkage (regardless of namespace)
  • extern "C" forces a function to have external linkage (cannot make it static) See Richard's comment: static inside extern "C" is valid; an entity so declared has internal linkage, and so does not have a language linkage
  • Linkage from C++ to objects defined in other languages and to objects defined in C++ from other languages is implementation-defined and language-dependent. Only where the object layout strategies of two language implementations are similar enough can such linkage be achieved
往事随风而去 2024-08-01 14:43:46

只是想添加一些信息,因为我还没有看到它发布。

您经常会在 C 头文件中看到如下代码:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

这样做的目的是允许您将 C 头文件与 C++ 代码一起使用,因为将定义宏 __cplusplus 。 但您仍然可以将其与旧版 C 代码一起使用,其中宏定义,因此它不会看到唯一的 C++ 构造。

尽管如此,我也见过 C++ 代码,例如:

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

我想它可以完成同样的事情。

不确定哪种方式更好,但两种方式我都见过。

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:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

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:

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

which I imagine accomplishes much the same thing.

Not sure which way is better, but I have seen both.

揪着可爱 2024-08-01 14:43:46

反编译 g++ 生成的二进制文件以查看发生了什么

main.cpp

void f() {}
void g();

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

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

编译和反汇编生成的 ELF 输出:

g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o

输出包含:

     8: 0000000000000000     7 FUNC    GLOBAL DEFAULT    1 _Z1fv
     9: 0000000000000007     7 FUNC    GLOBAL DEFAULT    1 ef
    10: 000000000000000e    17 FUNC    GLOBAL DEFAULT    1 _Z1hv
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv
    13: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND eg

解释

我们看到:

  • efeg 存储在与代码中同名的符号中

  • 其他符号被破坏。 让我们来整理一下它们:

    <前><代码>$ c++filt _Z1fv
    F()
    $ c++filt _Z1hv
    H()
    $ c++filt _Z1gv
    G()

结论:以下两种符号类型都没有被破坏:

  • 定义
  • 已声明但未定义 (Ndx = UND),将在链接或运行时从另一个提供目标文件

因此,在调用以下内容时,您将需要 extern "C"

  • C from C++:告诉 g++ 期望由 gcc
  • C++ 生成的未损坏的符号C:告诉g++gcc生成未损坏的符号以使用

在extern C中不起作用的东西

很明显,任何需要的C++功能名称修改在 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++ 示例中的最小可运行 C

为了完整起见,对于新手来说,另请参阅:如何在 C++ 项目中使用 C 源文件?

调用C++ 中的 C 非常简单:每个 C 函数只有一个可能的非重整符号,因此不需要额外的工作。

main.cpp

#include <cassert>

#include "c.h"

int main() {
    assert(f() == 1);
}

c.h

#ifndef C_H
#define C_H

/* This ifdef allows the header to be used from both C and C++ 
 * because C does not know what this extern "C" thing is. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif

#endif

c.c

#include "c.h"

int f(void) { return 1; }

运行:

g++ -c -o main.o -std=c++98 main.cpp
gcc -c -o c.o -std=c89 c.c
g++ -o main.out main.o c.o
./main.out

如果没有 extern "C",链接将失败:

main.cpp:6: undefined reference to `f()'

因为 g++ 期望找到一个损坏的 f,其中 >gcc 没有产生。

GitHub 上的示例

从 C 示例中最小化可运行的 C++

从 C 调用 C++ 有点困难:我们必须手动创建我们想要公开的每个函数的非损坏版本。

这里我们说明如何向 C 公开 C++ 函数重载。

main.c

#include <assert.h>

#include "cpp.h"

int main(void) {
    assert(f_int(1) == 2);
    assert(f_float(1.0) == 3);
    return 0;
}

cpp.h

#ifndef CPP_H
#define CPP_H

#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif
int f_int(int i);
int f_float(float i);
#ifdef __cplusplus
}
#endif

#endif

cpp.cpp

#include "cpp.h"

int f(int i) {
    return i + 1;
}

int f(float i) {
    return i + 2;
}

int f_int(int i) {
    return f(i);
}

int f_float(float i) {
    return f(i);
}

运行:

gcc -c -o main.o -std=c89 -Wextra main.c
g++ -c -o cpp.o -std=c++98 cpp.cpp
g++ -o main.out main.o cpp.o
./main.out

如果没有 extern "C",它会失败:

main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'

因为 g++ 生成损坏gcc 找不到的符号。

GitHub 上的示例

当我包含来自 C++ 的 C 标头时,extern "c" 在哪里?

在 Ubuntu 18.04 中测试。

Decompile a g++ generated binary to see what is going on

main.cpp

void f() {}
void g();

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

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

Compile and disassemble the generated ELF output:

g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o

The output contains:

     8: 0000000000000000     7 FUNC    GLOBAL DEFAULT    1 _Z1fv
     9: 0000000000000007     7 FUNC    GLOBAL DEFAULT    1 ef
    10: 000000000000000e    17 FUNC    GLOBAL DEFAULT    1 _Z1hv
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv
    13: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND eg

Interpretation

We see that:

  • ef and eg were stored in symbols with the same name as in the code

  • the other symbols were mangled. Let's unmangle them:

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

Conclusion: both of the following symbol types were not mangled:

  • defined
  • declared but undefined (Ndx = UND), to be provided at link or run time from another object file

So you will need extern "C" both when calling:

  • C from C++: tell g++ to expect unmangled symbols produced by gcc
  • C++ from C: tell g++ to generate unmangled symbols for gcc to use

Things that do not work in extern C

It becomes obvious that any C++ feature that requires name mangling will not work inside 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) { }
}

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

#include <cassert>

#include "c.h"

int main() {
    assert(f() == 1);
}

c.h

#ifndef C_H
#define C_H

/* This ifdef allows the header to be used from both C and C++ 
 * because C does not know what this extern "C" thing is. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif

#endif

c.c

#include "c.h"

int f(void) { return 1; }

Run:

g++ -c -o main.o -std=c++98 main.cpp
gcc -c -o c.o -std=c89 c.c
g++ -o main.out main.o c.o
./main.out

Without extern "C" the link fails with:

main.cpp:6: undefined reference to `f()'

because g++ expects to find a mangled f, which gcc 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

#include <assert.h>

#include "cpp.h"

int main(void) {
    assert(f_int(1) == 2);
    assert(f_float(1.0) == 3);
    return 0;
}

cpp.h

#ifndef CPP_H
#define CPP_H

#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif
int f_int(int i);
int f_float(float i);
#ifdef __cplusplus
}
#endif

#endif

cpp.cpp

#include "cpp.h"

int f(int i) {
    return i + 1;
}

int f(float i) {
    return i + 2;
}

int f_int(int i) {
    return f(i);
}

int f_float(float i) {
    return f(i);
}

Run:

gcc -c -o main.o -std=c89 -Wextra main.c
g++ -c -o cpp.o -std=c++98 cpp.cpp
g++ -o main.out main.o cpp.o
./main.out

Without extern "C" it fails with:

main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'

because g++ generated mangled symbols which gcc cannot find.

Example on GitHub.

Where is the extern "c" when I include C headers from C++?

Tested in Ubuntu 18.04.

江心雾 2024-08-01 14:43:46

在每个 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() and dlopen() for calling such functions.

赠意 2024-08-01 14:43:46

C++ 修改函数名称以从过程语言创建面向对象的语言

大多数编程语言都不是构建在现有编程语言之上的。 C++ 建立在 C 之上,而且它是一种基于过程编程语言构建的面向对象的编程语言,因此存在像 extern "C" 这样的 C++ 表达式,它们提供了向后兼容性C.

让我们看下面的例子:

#include <stdio.h>
    
// Two functions are defined with the same name
//   but have different parameters

void printMe(int a) {
  printf("int: %i\n", a);
}

void printMe(char a) {
  printf("char: %c\n", a);
}
    
int main() {
  printMe('a');
  printMe(1);
  return 0;
}

AC 编译器不会编译上面的例子,因为同一个函数 printMe 被定义了两次(即使它们有不同的参数 int a vs 字符a)。

gcc -o printMe printMe.c && ./printMe;
1 个错误。 PrintMe 被定义多次。

但是,C++ 编译器将编译上面的示例。 它并不关心 printMe 被定义了两次。

g++ -o printMe printMe.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:

#include <stdio.h>
    
// Two functions are defined with the same name
//   but have different parameters

void printMe(int a) {
  printf("int: %i\n", a);
}

void printMe(char a) {
  printf("char: %c\n", a);
}
    
int main() {
  printMe('a');
  printMe(1);
  return 0;
}

A C compiler will not compile the above example, because the same function printMe is defined twice (even though they have different parameters int a vs char a).

gcc -o printMe printMe.c && ./printMe;
1 error. PrintMe is defined more than once.

However, a C++ compiler will compile the above example. It does not care that printMe is defined twice.

g++ -o printMe printMe.c && ./printMe;

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 includes 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);

可是我不能没有你 2024-08-01 14:43:46

并不是任何 C 头文件都可以通过仅包装在 extern“C”中而与 C++ 兼容。 当 C 标头中的标识符与 C++ 关键字冲突时,C++ 编译器会对此进行抱怨。

例如,我看到以下代码在 g++ 中失败:

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

有点道理,但在将 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++ :

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

Kinda makes sense, but is something to keep in mind when porting C-code to C++.

爱的故事 2024-08-01 14:43:46

它以可以从 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.

夏の忆 2024-08-01 14:43:46

它通知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.

诗酒趁年少 2024-08-01 14:43:46

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.

甜警司 2024-08-01 14:43:46

extern "C" 是一个链接规范,用于在Cpp 源文件调用C 函数。 我们可以调用 C 函数,编写变量,& 包含标题。 函数在 extern 实体 & 中声明 它是在外部定义的。 语法为

类型1:

extern "language" function-prototype

类型2:

extern "language"
{
     function-prototype
};

例如:

#include<iostream>
using namespace std;

extern "C"
{
     #include<stdio.h>    // Include C Header
     int n;               // Declare a Variable
     void func(int,int);  // Declare a function (function prototype)
}

int main()
{
    func(int a, int b);   // Calling function . . .
    return 0;
}

// Function definition . . .
void func(int m, int n)
{
    //
    //
}

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 is

Type 1:

extern "language" function-prototype

Type 2:

extern "language"
{
     function-prototype
};

eg:

#include<iostream>
using namespace std;

extern "C"
{
     #include<stdio.h>    // Include C Header
     int n;               // Declare a Variable
     void func(int,int);  // Declare a function (function prototype)
}

int main()
{
    func(int a, int b);   // Calling function . . .
    return 0;
}

// Function definition . . .
void func(int m, int n)
{
    //
    //
}
奶气 2024-08-01 14:43:46

我之前对 dll(动态链接库)文件使用了 'extern "C"' 来使 main() 等函数“可导出”,以便稍后可以在 dll 的另一个可执行文件中使用它。
也许我曾经使用过它的例子会很有用。

动态

#include <string.h>
#include <windows.h>

using namespace std;

#define DLL extern "C" __declspec(dllexport)
//I defined DLL for dllexport function
DLL main ()
{
    MessageBox(NULL,"Hi from DLL","DLL",MB_OK);
}

链接库

#include <string.h>
#include <windows.h>

using namespace std;

typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll
Function mainDLLFunc;//make a variable for function placeholder

int main()
{
    char winDir[MAX_PATH];//will hold path of above dll
    GetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe
    strcat(winDir,"\\exmple.dll");//concentrate dll name with path
    HINSTANCE DLL = LoadLibrary(winDir);//load example dll
    if(DLL==NULL)
    {
        FreeLibrary((HMODULE)DLL);//if load fails exit
        return 0;
    }
    mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL, "main");
    //defined variable is used to assign a function from dll
    //GetProcAddress is used to locate function with pre defined extern name "DLL"
    //and matcing function name
    if(mainDLLFunc==NULL)
    {
        FreeLibrary((HMODULE)DLL);//if it fails exit
        return 0;
    }
    mainDLLFunc();//run exported function 
    FreeLibrary((HMODULE)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

#include <string.h>
#include <windows.h>

using namespace std;

#define DLL extern "C" __declspec(dllexport)
//I defined DLL for dllexport function
DLL main ()
{
    MessageBox(NULL,"Hi from DLL","DLL",MB_OK);
}

EXE

#include <string.h>
#include <windows.h>

using namespace std;

typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll
Function mainDLLFunc;//make a variable for function placeholder

int main()
{
    char winDir[MAX_PATH];//will hold path of above dll
    GetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe
    strcat(winDir,"\\exmple.dll");//concentrate dll name with path
    HINSTANCE DLL = LoadLibrary(winDir);//load example dll
    if(DLL==NULL)
    {
        FreeLibrary((HMODULE)DLL);//if load fails exit
        return 0;
    }
    mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL, "main");
    //defined variable is used to assign a function from dll
    //GetProcAddress is used to locate function with pre defined extern name "DLL"
    //and matcing function name
    if(mainDLLFunc==NULL)
    {
        FreeLibrary((HMODULE)DLL);//if it fails exit
        return 0;
    }
    mainDLLFunc();//run exported function 
    FreeLibrary((HMODULE)DLL);
}
梦在夏天 2024-08-01 14:43:46

这个答案是为了不耐烦/有最后期限要满足的人,只有部分/简单的解释如下:

  • 在C++中,你可以通过重载在类中具有相同的名称(例如,因为它们都是相同的名称,所以不能导出为-来自dll等)解决这些问题的方法是将它们转换为不同的字符串(称为符号),符号占函数的名称,也是参数,因此每个函数即使具有相同的名称,也可以被唯一标识(也称为名称修饰)
  • 在 C 中,您没有重载,函数名称是唯一的(因此,不需要用于唯一标识函数名称的单独字符串,因此符号就是函数名称本身)

所以
在 C++ 中,名称修饰唯一标识每个函数
在 C 中,即使没有名称修改,每个函数都有唯一标识

要更改 C++ 的行为,即指定特定函数不应该发生名称修改,您可以使用 extern "C " 在函数名称之前,无论出于何种原因,例如从 dll 中导出具有特定名称的函数以供其客户端使用。

阅读其他答案,以获得更详细/更正确的答案。

This answer is for the impatient/ have deadlines to meet to, only a part/simple explanation is below:

  • in C++, you can have same name in class via overloading (for example, since they are all same name can't be exported as-is from dll, etc.) solution to these problems is they are converted to different strings (called symbols), symbols accounts the name of function, also the arguments, so each of these functions even with same name, can be uniquely identified (also called, name mangling)
  • in C, you don't have overloading, the function name is unique (so, a separate string for identifying the a function name uniquely is not required, so symbol is function name itself)

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.

属性 2024-08-01 14:43:46

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++ 中编译,这允许函数重载:

#include <stdio.h>

const char* get_message2(void); 
int get_message2(char*); 

int main() {
   char test[] = "BBBBBBB";
   get_message2(test);
   return 0;
}

const char* get_message2() {
    char test[] = "AAAAAAA";
    return "message2";
};

int get_message2(char* arg) {
    char test[] = "CCCCCCC";
   return 0;
}

在 C++ 中,函数名称不是唯一的符号标识符,C++ 编译器添加“前缀”和“后缀”基于函数原型的符号名称:

drazen@HP-ProBook-640G1:~/proba$ readelf  -a proba | grep message
    34: 0000000000001199    70 FUNC    GLOBAL DEFAULT   16 _Z12get_message2v
    36: 00000000000011df    72 FUNC    GLOBAL DEFAULT   16 _Z12get_message2Pc

但是如果我们希望我们的函数有一个 C 链接,我们必须添加 extern "C" 修饰符

const char* get_message2(void); 
extern "C" {
   int get_message2(char*); 
}

现在我们的 int get_message2(char*); 函数有一个旧的 C 链接:

drazen@HP-ProBook-640G1:~/proba$ readelf  -a proba | grep message
    32: 00000000000011df    72 FUNC    GLOBAL DEFAULT   16 get_message2
    35: 0000000000001199    70 FUNC    GLOBAL DEFAULT   16 _Z12get_message2v

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:

#include <stdio.h>

const char* get_message2(void); 
int get_message2(char*); 

int main() {
   char test[] = "BBBBBBB";
   get_message2(test);
   return 0;
}

const char* get_message2() {
    char test[] = "AAAAAAA";
    return "message2";
};

int get_message2(char* arg) {
    char test[] = "CCCCCCC";
   return 0;
}

In C++ function names are not unique symbol identifiers, and C++ compiler adds "prefixes" and "suffixes" to symbol name based on function prototype:

drazen@HP-ProBook-640G1:~/proba$ readelf  -a proba | grep message
    34: 0000000000001199    70 FUNC    GLOBAL DEFAULT   16 _Z12get_message2v
    36: 00000000000011df    72 FUNC    GLOBAL DEFAULT   16 _Z12get_message2Pc

But if we want for our function have a C linkage, we must add extern "C" modifier

const char* get_message2(void); 
extern "C" {
   int get_message2(char*); 
}

Now our int get_message2(char*); function has an old C linkage:

drazen@HP-ProBook-640G1:~/proba$ readelf  -a proba | grep message
    32: 00000000000011df    72 FUNC    GLOBAL DEFAULT   16 get_message2
    35: 0000000000001199    70 FUNC    GLOBAL DEFAULT   16 _Z12get_message2v
╰◇生如夏花灿烂 2024-08-01 14:43:46

有趣的是,Visual Studio 和 dumpbin 都没有出现在这个线程中(到目前为止)。 所以我也想添加一些有关此的信息。
如果有意义,我们也可以将其合并到上述任何答案中。我不确定这里的 SO 哲学。


使用 Visual Studio 编译时编译器中,每个 extern "C" 符号前面都有一个前导下划线
示例:

extern "C"
void cf () {}

void cppf () {}

在生成的对象上编译并运行 dumpbin /symbols,符号如下所示:

01A 00000000 SECT7  notype ()    External     | _cf
01B 00000000 SECT5  notype ()    External     | ?cppf@@YAXXZ (void __cdecl cppf(void))

变量相同。

typedef struct {
  int a;
  int b;
}
CppStruct;

extern "C" {
  typedef struct {
    int a;
    int b;
    int c;
  }
  CStruct;
  CStruct cCStruct;
  CppStruct cCppStruct;
}
CStruct cppCStruct;
CppStruct cppCppStruct;

dumpbin /symbols

009 00000000 SECT3  notype       External     | _cCStruct
00A 0000000C SECT3  notype       External     | _cCppStruct
00B 00000014 SECT3  notype       External     | ?cppCStruct@@3UCStruct@@A (struct CStruct cppCStruct)
00C 00000020 SECT3  notype       External     | ?cppCppStruct@@3UCppStruct@@A (struct CppStruct cppCppStruct)

旁注:对于 C++ 符号,dumpbin 还会在括号中显示未损坏的符号名称。
旁注 2:如您所见,extern "C" 不会影响类型定义。


小心! 如果一个变量之前某处声明而没有extern "C"(例如在头文件中),那么它将使用 C++ 链接进行编译,恕不另行通知:

extern CppStruct ifcStruct;
extern int       ifcVar;
/* ... */

extern "C"
{
CppStruct ifcStruct;
int       ifcVar = 0;
}

dumpbin /symbols

00C 00000000 SECT4  notype       External     | ?ifcStruct@@3UCppStruct@@A (struct CppStruct ifcStruct)
00D 00000008 SECT4  notype       External     | ?ifcVar@@3HA (int ifcVar)

但是,当函数之前某处声明时> extern "C",那么 (Microsoft) 编译器会给出明显的错误消息:(

test.cpp(20): error C2732: linkage specification contradicts earlier specification for 'ifcf'
test.cpp(20): note: see declaration of 'ifcf'

讨论了这种差异的原因 此处。)


据我所知,extern "C"告诉编译器使用 C 调用约定
另请参阅 从 DLL 导出 C++ 类 - Eli Bendersky 的网站Google 仍然找到它,但该网站似乎已经死了):

extern "C" __declspec(dllexport) IKlass* __cdecl create_klass()
让我们按顺序看看每个部分的含义:

  • extern "C" - 告诉 C++ 编译器链接器应该为此函数使用 C 调用约定和名称修饰。 名称本身从未损坏的 DLL 中导出 (create_klass)
  • __declspec(dllexport) - 告诉链接器从 DLL 导出 create_klass 符号。 或者,可以将名称 create_klass 放置在提供给链接器的 .def 文件中。
  • __cdecl - 重复使用 C 调用约定(与 __stdcall 调用约定相反)。 这里并不是绝对必要的,但为了完整性我将其包含在内(也在应用程序代码中的 iklass_factory 的 typedef 中)。

另请参阅此 常见问题解答:如何混合 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

extern "C"
void cf () {}

void cppf () {}

and running dumpbin /symbols on the resulting object, the symbols looks as following:

01A 00000000 SECT7  notype ()    External     | _cf
01B 00000000 SECT5  notype ()    External     | ?cppf@@YAXXZ (void __cdecl cppf(void))

Same for variables.

typedef struct {
  int a;
  int b;
}
CppStruct;

extern "C" {
  typedef struct {
    int a;
    int b;
    int c;
  }
  CStruct;
  CStruct cCStruct;
  CppStruct cCppStruct;
}
CStruct cppCStruct;
CppStruct cppCppStruct;

dumpbin /symbols

009 00000000 SECT3  notype       External     | _cCStruct
00A 0000000C SECT3  notype       External     | _cCppStruct
00B 00000014 SECT3  notype       External     | ?cppCStruct@@3UCStruct@@A (struct CStruct cppCStruct)
00C 00000020 SECT3  notype       External     | ?cppCppStruct@@3UCppStruct@@A (struct CppStruct cppCppStruct)

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:

extern CppStruct ifcStruct;
extern int       ifcVar;
/* ... */

extern "C"
{
CppStruct ifcStruct;
int       ifcVar = 0;
}

dumpbin /symbols

00C 00000000 SECT4  notype       External     | ?ifcStruct@@3UCppStruct@@A (struct CppStruct ifcStruct)
00D 00000008 SECT4  notype       External     | ?ifcVar@@3HA (int ifcVar)

However, when a function is declared somewhere before without extern "C", then the (Microsoft) compiler gives a distinct error message:

test.cpp(20): error C2732: linkage specification contradicts earlier specification for 'ifcf'
test.cpp(20): note: see declaration of 'ifcf'

(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):

extern "C" __declspec(dllexport) IKlass* __cdecl create_klass()
Let's see what each part means, in order:

  • extern "C" - tells the C++ compiler that the linker should use the C calling convention and name mangling for this function. The name itself is exported from the DLL unmangled (create_klass)
  • __declspec(dllexport) - tells the linker to export the create_klass symbol from the DLL. Alternatively, the name create_klass can be placed in a .def file given to the linker.
  • __cdecl - repeats that the C calling convention (opposite of __stdcall calling convention) is to be used. It's not strictly necessary here, but I include it for completeness (in the typedef for iklass_factory in the application code as well).

see also this FAQ: How to mix C and C++

逆光下的微笑 2024-08-01 14:43:46

当混合 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++.

溺ぐ爱和你が 2024-08-01 14:43:46

在不与其他好的答案冲突的情况下,我将添加一些我的例子。

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

复古式 2024-08-01 14:43:46

gcc 最近似乎也支持名称修改。 即使在 extern "c" 内部,如果您使用类或重载,它也会自动损坏。

#include <stdio.h>
extern "C"{


struct myint{
    int i;
};

struct myint2
{
    int a;
    myint2(int a): a(a) {};
    operator myint() const {return myint{a};}
};

}

void f1(myint i){
    printf("%d", i.i);
}

int main(){
    myint2 a(1);
    f1(a);
}

我什至使用了很多 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.

#include <stdio.h>
extern "C"{


struct myint{
    int i;
};

struct myint2
{
    int a;
    myint2(int a): a(a) {};
    operator myint() const {return myint{a};}
};

}

void f1(myint i){
    printf("%d", i.i);
}

int main(){
    myint2 a(1);
    f1(a);
}

I even used many cpp feature. but the code compiles and runs ok. if you nm, you can see main is not mangled, but myint is.

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