探索C++的秘密之详解extern

探索C++的秘密之详解extern,第1张

探索C++的秘密之详解extern,第2张

我经常在cpp代码中看到这样的代码:

# ifdef _ _ cplusplus
extern " C " {
# endif

//一段代码

# ifdef _ _ cplusplus
}
# endif

这个代码到底是什么意思?首先,__cplusplus是cpp中的自定义宏。如果定义了这个宏,就意味着它是一段cpp代码。换句话说,上面这段代码的意思是:如果是一段cpp代码,那么加入extern“C”{ and }来处理代码。

为了理解为什么使用extern“C ”,我们必须从cpp中的函数重载开始。在c++中,为了支持重载机制,在编译后的汇编代码中,要对函数的名字进行处理,比如函数的返回类型等。然而在C中,它只是一个简单的函数名,不会添加任何其他信息。也就是说,C++和C对生成的函数名的处理是不同的。

比如下面这个简单的函数,我们来看看添加或者不添加extern“C”产生的汇编代码的变化:

int f(void)
{
return 1;
}

添加extern "C "时生成的汇编代码是:

。文件“test . cxx”
。文本
。对齐2
。globl _f
。def _ f;。SCL 2;。32型;。endef
_ f:
pushl % ebp
movl % esp,%ebp
movl ,%eax
popl %ebp
ret

但是在加入了extern C之后

。文件“test . cxx”
。文本
。对齐2
。globl __Z1fv
。def _ _ Z1fv。SCL 2;。32型;。endef
_ _ Z1fv:
pushl % ebp
movl % esp,%ebp
movl ,%eax
popl %ebp
ret

两段汇编代码也是用gcc -S命令生成的,所有地方都一样,除了生成的函数名,一个是_f,一个是__Z1fv。

为了理解添加或不添加extern“C”对函数名的影响,我们继续我们的讨论:为什么我们需要使用extern“C”?C++之父在设计C++的时候,考虑到当时已经有大量的C代码存在。为了支持原C代码和编写的C库,需要在C++中尽可能地支持C,extern“C”就是策略之一。

想象一下这种情况:一个库文件已经用C写好了,运行良好。这个时候,我们需要使用这个库文件,但是我们需要使用C++来编写这个新代码。如果这段代码使用C++链接这个C库文件,那么就会出现链接错误。我们来看一段代码:首先我们用C的处理方法写一个函数,也就是假设这个函数当时是用C写的:

//f1 . C
extern " C "
{
void f1()
{
return;
}
}

命令是:gcc -c f1.c -o f1.o生成一个名为f1.o的库文件,再写一段代码调用这个f1函数:

// test.cxx
//这个extern表示f1函数是在别的地方定义的,这样就可以用
//编译,但是链接
时还是需要链接上的原库文件。
extern void f1();

int main()
{
f1();

返回0;
}

通过gcc -c test.cxx -o test.o生成一个名为test.o的文件然后,我们用gcc test.o f1.o链接两个文件,但是出错了。错误提示是:

test.o(。text + 0x1f):test.cxx:取消定义对“f1()”的引用

也就是说,编译test.cxx时,编译器用C++处理f1()函数,但实际上链接库文件用C处理函数,所以会出现链接过不去的错误:链接器找不到函数。

所以,为了在C++代码中调用用C写的库文件,需要用extern“C”告诉编译器这是用C写的库文件,请用C的方式链接。

例如,现在我们有了一个C库文件,它的头文件是f.h,生成的lib文件是f.lib,那么如果我们想在C++中使用这个库文件,我们需要这样写:

extern " C "
{
# include " f . h "
}

回到上面的问题,如果要纠正链接错误,需要这样重写test.cxx:

extern " C "
{
extern void f1();
}

int main()
{
f1();

返回0;
}

重新编译链接。

摘要

C++和C++处理函数的方式不同。extern“C”是使c++能够调用用C编写的库文件的手段,如果要提示编译器处理C中的函数,就要用extern“C”来解释。

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » 探索C++的秘密之详解extern

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情