★extern "C"
先来看一下下面这道题:
在c++程序中被c编译器编译后的函数,为什么要加extern “C”?
我们来写一个简单的程序测试一下。
先写一个c++程序,简单的求两个数的和。
<span style="font-size:14px;">#include<iostream>
using namespace std;
extern int add(int ,int);
int main()
{
int a=1;
int b=2;
int ret=add(1,2);
cout<<ret<<endl;
}</span>
再在一个c文件中写add函数。
<span style="font-size:14px;">int add(int x,int y)
{
return x+y;
}</span>
调试的时候我们就会发现这个程序在编译的时候是没有问题的,在链接的时候会提示以下错误:
1>test.obj : error LNK2019: 无法解析的外部符号 "int __cdecl add(int,int)" (?add@@YAHHH@Z),该符号在函数 _main 中被引用。
(其中_cdecl是一种调用约定。)
这是为什么呢?其实C与C++的命名方式是不同的,C语言中,函数的命名方式比较简单,在函数名前加一个_。而在C++中,函数的命名方式后面还会跟一些特殊符号和字母。
比如:
这是在C语言中add函数的命名方式:
而在C++中,add函数是这样命名的:
所以,在C++中,调用函数时编译器是下面这种命名方式C++来链接函数的,所以上面的程序是找不着对应的add函数的。这个时候,extern "C"就有用了,我们在C++文件中这样声明:
extern “C” int add(int x,int y);
就不会出现上面那种错误了。
extern “C”的作用就是告诉编译器,这是用C的方式写成的库文件,请用C的方式来链接它。C和C++对函数的处理方式是不同的.extern "C"是使C++能够调用C写作的库文件的一个手段,如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern "C"来说明。
下面给出问题的正确答案,以做参考。
C语言不支持函数重载,C++支持函数重载,函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型:void foo(int x,int y);该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int _int之类的名字。C++提供了C链接交换指定符号extern"C"来解决名字匹配问题。
★函数重载机制
看下面这段代码:
<span style="font-size:14px;">#include<iostream>
using namespace std;
extern int add(int, int);
float add(float a,float b)
{
return a + b;
}
int add(int a, int b)
{
return a + b;
}
int main()
{
int a = 10;
int b = 20;
float c = 1.0;
float d = 2.0;
float ret1 = add(a, b);
int ret2 = add(a, b);
cout << ret1<< endl;
cout << ret2 << endl;
return 0;
}</span>
在调用函数时,编译器怎么知道调用哪个函数呢?这是因为函数重载机制,在程序编译时,编译器会对函数做一些处理,对函数名相同的函数进行“重命名”,如下:
我们可以看到,两个函数的名字并不是相同的,编译器在调用函数时根据参数,返回值来获取调用对象。
add@@YAHHH@Z:@@YA表示起始,HHH表示参数参数返回值,H代表int类型,M代表float类型,@Z重命名结束。
当然,不同的编译器重命名的方式也是不同的(可以查看相应的调用约定),了解在这个过程中发生了什么事就可以了。