外部"C"声明如何工作?

sam*_*moz 27 c c++ extern-c

我正在学习编程语言课程,我们正在谈论extern "C"声明.

除了"它接口C和C++"之外,这个声明如何在更深层次上工作?这又如何影响程序中发生的绑定?

Ber*_*ron 47

extern "C"用于确保后面的符号没有修饰(装饰).


例:

假设我们在一个名为的文件中有以下代码test.cpp:

extern "C" {
  int foo() {
    return 1;
  }
}

int bar() {
  return 1;
}
Run Code Online (Sandbox Code Playgroud)

如果你跑 gcc -c test.cpp -o test.o

看看符号名称:

00000010 T _Z3barv

00000000 T foo

foo() 保守名字.

  • 此函数也使用C调用约定,如果它不同于C++调用约定.它可能没有,对于C++函数,它只采用C作为参数的类型,但使用`extern"C"`它肯定不会. (8认同)

Cth*_*utu 25

让我们看一下可以在C和C++中编译的典型函数:

int Add (int a, int b)
{
    return a+b;
}
Run Code Online (Sandbox Code Playgroud)

现在在C中,该函数在内部称为"_Add".而使用名为name-mangling的系统在内部完全不同地调用C++函数.它基本上是一种命名函数的方法,以便具有不同参数的相同函数具有不同的内部名称.

因此,如果在add.c中定义了Add(),并且你在add.h中有原型,那么如果你试图在一个C++文件中包含add.h,你就会遇到问题.因为C++代码正在寻找名称与add.c中的名称不同的函数,所以会出现链接器错误.要解决该问题,您必须通过此方法包含add.c:

extern "C"
{
#include "add.h"
}
Run Code Online (Sandbox Code Playgroud)

现在,C++代码将链接到_Add而不是C++名称的版本.

这是表达式的一个用途.最重要的是,如果你需要在C++程序中编译严格的C代码(通过include语句或其他方法),你需要用extern"C"{...}声明来包装它.

  • 这里最好的答案.值得投票. (5认同)
  • 领先的`_`是编译器/链接器/系统相关的. (2认同)

Ree*_*sey 9

当您使用extern"C"标记代码块时,您告诉系统使用C样式链接.

这主要影响链接器破坏名称的方式.您可以从链接器中获得标准的C风格命名,而不是使用C++样式名称修改(支持运算符重载更复杂).


gil*_*rtc 5

在C++中,函数的名称/符号实际上被重命名为其他类,以便不同的类/名称空间可以具有相同签名的函数.在C中,功能全局定义,不需要这样的自定义重命名过程.

为了使C++和C相互通信,"extern C"指示编译器不要使用C约定.

  • 有关名称修改的更多信息,请访问维基百科:http://en.wikipedia.org/wiki/Name_mangling (4认同)

qua*_*ana 5

extern C 影响 C++ 编译器的名称修改。它是一种让 C++ 编译器不破坏名称的方法,或者更确切地说,以与 C 编译器相同的方式破坏名称。这就是 C 和 C++ 的接口方式。

举个例子:

extern "C" void foo(int i);
Run Code Online (Sandbox Code Playgroud)

将允许该函数在 C 模块中实现,但允许从 C++ 模块中调用它。

当试图让 C 模块调用 C++ 模块中定义的 C++ 函数(显然 C 不能使用 C++ 类)时,就会出现麻烦。C 编译器不喜欢extern "C".

所以你需要使用这个:

#ifdef __cplusplus
extern "C" {
#endif

void foo(int i);

#ifdef __cplusplus
}
#endif
Run Code Online (Sandbox Code Playgroud)

现在,当它出现在头文件中时,C 和 C++ 编译器都会对该声明感到满意,并且现在可以在 C 或 C++ 模块中定义它,并且可以由 C 和 C++ 代码调用。


Joh*_*itb 5

应该注意,extern "C"还修改了函数的类型.它不仅修改较低级别的内容:

extern "C" typedef void (*function_ptr_t)();

void foo();

int main() { function_ptr_t fptr = &foo; } // error!
Run Code Online (Sandbox Code Playgroud)

类型&foo不等于typedef指定的类型(尽管代码被某些编译器接受,但并非所有编译器都接受).