C++ 何时使用extern“;C";?

C++ 何时使用extern“;C";?,c++,extern-c,C++,Extern C,我知道如何使用extern“C”,但您必须使用它的条件是什么 >代码>外部“C”告诉C++编译器不要执行任何名称修改。 大括号中的代码。这允许您从调用C函数 在C++中。< /P> 例如: #include <string.h> int main() { char s[] = "Hello"; char d[6]; strcpy_s(d, s); } #包括 int main() { char s[]=“你好”; chard[6]; strcpy_s(

我知道如何使用
extern“C”
,但您必须使用它的条件是什么

<> >代码>外部“C”<代码>告诉C++编译器不要执行任何名称修改。 大括号中的代码。这允许您从调用C函数 在C++中。< /P> 例如:

#include <string.h>

int main()
{
    char s[] = "Hello";
    char d[6];

    strcpy_s(d, s);
}
#包括
int main()
{
char s[]=“你好”;
chard[6];
strcpy_s(d,s);
}
而这在VC++上编译得很好。但有时这被写为:

extern "C" {   
#include <string.h>  
}
extern“C”{
#包括
}

我看不出有什么意义。您能给出一个需要
extern“C”
的实例吗?

从库导出函数时,
extern“C”
的一个非常常见的用法。如果你不禁用C++名字的限制,你可以很难让你的库的客户命名你的函数。同样地,当你在另一个方向上,当你导入了一个用C链接导出的函数时,

你使用 Extn(c)防止头文件中的名字和你已经被编译的库或对象的C++对象文件不被篡改。 例如,假设您有一个
小部件
库,它是用C编译器编译的,这样它发布的接口就不会损坏

如果您将头文件原样包含到代码中,它将假定名称已损坏,并且您将告诉链接器要查找这些损坏的版本

但是,由于您需要类似于
function@intarray_float_charptr
小部件
库将只发布
函数
,您将遇到问题

但是,如果您将其包括在以下内容中:

extern "C" {
    #include "widget.h"
}
您的编译器将知道它应该尝试使用未损坏的
函数

这就是为什么,在C文件的头文件中,它被包含在C或C++程序中,你会看到类似的事情:

#ifdef __cplusplus
    extern "C" {
#endif

// Everything here works for both C and C++ compilers.

#ifdef __cplusplus
    }
#endif

如果使用C编译器包含此代码,则
#ifdef
行将导致
外部“C”
内容消失。对于C++编译器(在这里定义了代码< *-cPLUS PLUS < /COD> >),所有的东西都不会被破坏。

< P>当你连接到用C代码写的库> ExtNe/COD> >告诉编译器不要修饰名字,以便链接器可以找到函数。在C++函数名称中,对于链接器有信息,比如名称中包含的参数类型和大小。

< p>如果您正在生成二进制库A,则会公开一个您想从二进制B.< /P>调用的函数。 假设A是A.dll,B是B.exe,并且您在Windows系统上

C++没有描述二进制布局,因此B知道如何调用a。通常,通过使用相同的编译器生成a和B来解决此问题。如果您想要更通用的解决方案,请使用extern关键字。这将以C方式公开函数。C确实描述了一种二进制格式,因此来自不同编译器的不同二进制文件可以相互通信

见:

< p>如果在C++代码中,你<代码>包含了一个外部库的头部(用C编码),如果函数没有声明为代码>外“C”< /代码>,它们将不起作用(你将在链接时得到未定义的引用)。p> 但是现在,人们编写C库并为您提供头文件时往往会知道这一点,并且经常将
外部“C”
放在头文件中(使用
\ifdef\uucplusplus
进行适当保护)


也许更好的理解方法是使用(假设您有一个Linux系统)nm实用程序来显示库或可执行文件中使用的(未混合的)名称。

下面是一个具体的示例,说明了在哪些情况下会发生故障,需要
外部“C”
来修复

模块.h

int f(int arg);
int f(int arg) {
  return arg + 1;
}
#include "module.h"

int main() {
  f(42);
}
#ifdef __cplusplus
  extern "C" {
#endif

int f(int arg);

#ifdef __cplusplus
  }
#endif
模块.c

int f(int arg);
int f(int arg) {
  return arg + 1;
}
#include "module.h"

int main() {
  f(42);
}
#ifdef __cplusplus
  extern "C" {
#endif

int f(int arg);

#ifdef __cplusplus
  }
#endif
main.cpp

int f(int arg);
int f(int arg) {
  return arg + 1;
}
#include "module.h"

int main() {
  f(42);
}
#ifdef __cplusplus
  extern "C" {
#endif

int f(int arg);

#ifdef __cplusplus
  }
#endif
<>因为我在混合C和C++,这是不链接的(两个对象文件中,只有一个将知道代码< F/COD>在其C++的名称下)。 也许解决这个问题的最干净的方法是使头文件与C和C++兼容:

模块.h

int f(int arg);
int f(int arg) {
  return arg + 1;
}
#include "module.h"

int main() {
  f(42);
}
#ifdef __cplusplus
  extern "C" {
#endif

int f(int arg);

#ifdef __cplusplus
  }
#endif

使用
标题代替黑客攻击
以避免名称混乱。@AlexandreC.:使用
标题绝对不是一个好主意。
头不保证在全局命名空间中放置标识符,因此当代码移动到其他编译器时,会破坏依赖全局命名空间标识符的代码。另外,
头可能并且实际上会在全局名称空间中放置标识符,因此理论上也会破坏依赖于非污染全局名称空间的代码。“理论上”太模糊了;至少有一个白痴做了那件事。所以,不要使用..alfpStnbAc:如果你写C++,请使用<代码> <代码>。如果您编写C,请使用
并使用C编译器编译代码。混合语言不是个好主意。@AlexandreC:你的建议全是联想而不是推理。这很愚蠢。@AlfP.Steinbach:如果在从
头调用的每个函数前面加前缀
std::
,会怎么样?是否仍然存在可移植性问题?为什么还有第二个
#ifdef uu cplusplus
。@user974191:这是用于右大括号,否则大括号将不平衡。