语言链接是用于链接C++
和non-C++
代码片段之间的术语。通常,在 C++ 程序中,所有函数名、函数类型甚至变量名都具有默认的 C++ 语言链接。
C
可以使用预定义的链接说明符将 C++ 目标代码链接到使用其他源语言(如 )生成的另一个目标代码。
你必须知道 的概念name mangling
,它对函数名、函数类型和变量名进行编码,以便为它们生成一个唯一的名称。这允许链接器区分常用名称(如在函数重载的情况下)。将 C 模块与使用 C++ 编译器编译的库或目标文件链接时,不希望进行名称修改。为了防止这种情况下的名称修改,使用了链接说明符。在这种情况下,extern "C"
是链接说明符。举个例子(这里提到的c++代码):
typedef int (*pfun)(int); // line 1
extern "C" void foo(pfun); // line 2
extern "C" int g(int) // line 3
...
foo( g ); // Error! // line 5
第 1 行声明pfun
指向 C++ 函数,因为它缺少链接说明符。
因此,第 2 行将 foo 声明为一个 C 函数,它接受一个指向 C++ 函数的指针。
第 5 行尝试使用指向 g、C 函数、类型不匹配的指针调用 foo。
函数名称链接的差异:
让我们拿两个不同的文件:
一个有extern "c"
链接(file1.cpp):
#include <iostream>
using namespace std;
extern "C"
{
void foo (int a, int b)
{
cout << "here";
}
}
int main ()
{
foo (10,20);
return 0;
}
一个没有extern "c"
链接(file2.cpp):
#include <iostream>
using namespace std;
void foo (int a, int b)
{
cout << "here";
}
int main ()
{
foo (10,20);
return 0;
}
现在编译这两个并检查 objdump。
# g++ file1.cpp -o file1
# objdump -Dx file1
# g++ file2.cpp -o file2
# objdump -Dx file2
使用 extern "C" 链接,函数没有名称修饰foo
。因此,任何使用它的程序(假设我们用它制作了一个共享库)都可以直接调用 foo (使用 and 之类的辅助函数dlsym
)dlopen
,而无需考虑任何名称修改效果。
0000000000400774 <foo>:
400774: 55 push %rbp
400775: 48 89 e5 mov %rsp,%rbp
....
....
400791: c9 leaveq
400792: c3 retq
0000000000400793 <main>:
400793: 55 push %rbp
400794: 48 89 e5 mov %rsp,%rbp
400797: be 14 00 00 00 mov $0x14,%esi
40079c: bf 0a 00 00 00 mov $0xa,%edi
4007a1: e8 ce ff ff ff callq 400774 <foo>
4007a6: b8 00 00 00 00 mov $0x0,%eax
4007ab: c9 leaveq
另一方面,当 noextern "C"
被使用时, func:foo
被一些预定义的规则(正在使用的编译器/链接器已知)破坏,因此应用程序不能直接从中调用它,并将名称指定为foo
. 但是,如果您愿意,您可以使用损坏的名称(_Z3fooii
在这种情况下)来调用它,但出于显而易见的原因,没有人使用它。
0000000000400774 <_Z3fooii>:
400774: 55 push %rbp
400775: 48 89 e5 mov %rsp,%rbp
...
...
400791: c9 leaveq
400792: c3 retq
0000000000400793 <main>:
400793: 55 push %rbp
400794: 48 89 e5 mov %rsp,%rbp
400797: be 14 00 00 00 mov $0x14,%esi
40079c: bf 0a 00 00 00 mov $0xa,%edi
4007a1: e8 ce ff ff ff callq 400774 <_Z3fooii>
4007a6: b8 00 00 00 00 mov $0x0,%eax
4007ab: c9 leaveq
4007ac: c3 retq
这个页面也是这个特定主题的好读物。
关于调用约定的一篇很好且解释清楚的文章:http: //www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx