语言链接是指C++和非C++代码段之间的链接。通常,在C++程序中,所有函数名、函数类型甚至变量名都具有默认的C++语言链接。
C++目标代码可以使用预定义的链接说明符与使用其他源语言(如C)生成的另一个对象代码进行链接。
您可能已经了解到名称重整的概念,它对函数名、函数类型和变量名进行编码,以便为它们生成唯一的名称。这使得链接器能够区分常用名称(例如函数重载的情况)。名称重整在将C模块与使用C++编译器编译的库或目标文件链接时并不可取。为了防止这种情况下的名称重整,使用链接说明符。在这种情况下,extern "C" 是链接说明符。让我们举个例子(c++代码在这里提到:here):
typedef int (*pfun)(int)
extern "C" void foo(pfun)
extern "C" int g(int) // line 3
...
foo( g )
第一行声明 pfun
指向一个 C++ 函数,因为它缺少链接说明符。
因此,第二行声明 foo 是一个接受指向 C++ 函数的指针的 C 函数。
第五行试图用指向 C 函数 g 的指针调用 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。
使用extern "C"链接时,函数foo
不会进行名称重整。因此,任何使用它的程序(假设我们将其制作成共享库)都可以直接调用foo(使用像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
另一方面,如果没有使用extern "C"
,函数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
:)
。 - artyom.stv