将外部“C”放入C++代码中具体做什么?
例如:
extern "C" {
void foo();
}
将外部“C”放入C++代码中具体做什么?
例如:
extern "C" {
void foo();
}
当前回答
最近,gcc似乎也支持名称篡改。即使在外部“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是损坏的。
其他回答
extern“C”使C++中的函数名具有C链接(编译器不会损坏该名称),以便客户端C代码可以使用仅包含函数声明的C兼容头文件链接到(使用)您的函数。函数定义包含在二进制格式(由C++编译器编译)中,然后客户端C链接器将使用C名称链接到该格式。
由于C++有函数名的重载,而C没有,所以C++编译器不能仅将函数名用作链接到的唯一id,因此它通过添加有关参数的信息来破坏名称。C编译器不需要更改名称,因为您不能重载C中的函数名。当您声明函数在C++中具有外部“C”链接时,C++编译器不会将参数/参数类型信息添加到用于链接的名称中。
正如您所知,您可以显式指定每个单独声明/定义的外部“C”链接,或使用块将声明/定义序列分组以具有特定链接:
extern "C" void foo(int);
extern "C"
{
void g(char);
int i;
}
如果您关心技术细节,请参见C++03标准第7.5节,这里有一个简短的总结(重点是外部“C”):
外部“C”是链接规范每个编译器都需要提供“C”链接链接规范只能出现在命名空间范围内所有函数类型、函数名和变量名都有语言链接参见Richard的评论:只有具有外部链接的函数名和函数名才有语言链接具有不同语言链接的两种函数类型是不同的类型,即使在其他方面相同连杆规格嵌套,内部规格决定最终连杆类成员忽略外部“C”最多一个具有特定名称的函数可以具有“C”链接(无论名称空间如何)外部“C”强制一个函数具有外部链接(不能使其成为静态的)参见Richard的评论:外部内部的静态“C”是有效的;这样声明的实体具有内部链接,因此没有语言链接从C++到用其他语言定义的对象以及从其他语言到用C++定义的对象的链接是由实现定义的,并且依赖于语言。只有当两种语言实现的对象布局策略足够相似时,才能实现这种链接
在不与其他好答案冲突的情况下,我将添加一点我的示例。
C++编译器的作用是:它在编译过程中破坏了名称,因此我们需要告诉编译器要特别对待C实现。
当我们制作C++类并添加外部“C”时,我们告诉C++编译器我们正在使用C调用约定。
原因(我们从C++调用C实现):要么我们想从C++调用C++函数,要么从C调用C++函数(C++类…等在C中不起作用)。
我之前对dll(动态链接库)文件使用了“extern”C“”,以使etc.main()函数“可导出”,以便稍后可以在dll的另一个可执行文件中使用。也许一个我过去使用它的例子会很有用。
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);
}
请参阅下面的链接,该链接是Geeks for Geeks对外部“C”用法的解释。从下面的页面添加重要信息。
考虑函数f()的以下声明
int f (void) { return 1; }
int f (int) { return 0; }
void g (void) { int i = f(), j = f(0); }
C++编译器可能会将上述名称更改为以下名称(来源:Wiki)
int __f_v (void) { return 1; }
int __f_i (int) { return 0; }
void __g_v (void) { int i = __f_v(), j = __f_i(0); }
https://www.geeksforgeeks.org/extern-c-in-c/
在每个C++程序中,所有非静态函数都在二进制文件中表示为符号。这些符号是唯一标识程序中函数的特殊文本字符串。
在C中,符号名与函数名相同。这是可能的,因为在C中没有两个非静态函数可以具有相同的名称。
因为C++允许重载,并且具有许多C不具备的特性——比如类、成员函数、异常规范——所以不可能简单地使用函数名作为符号名。为了解决这个问题,C++使用了所谓的名称篡改,它将函数名和所有必要的信息(如参数的数量和大小)转换为一些只有编译器和链接器才能处理的奇怪字符串。
因此,如果将函数指定为extern C,编译器不会对其执行名称修改,而是可以直接使用其符号名作为函数名进行访问。
这在使用dlsym()和dlopen()调用此类函数时很方便。