原创 extern的用法小结

2010-7-27 22:40 1422 7 7 分类: MCU/ 嵌入式

有段时间看些源码,包括一些语法,很多都看不懂啊


 


 


extern的用法小结


 


[extern变量] [extern函数]解析


      这种情况下的extern说明变量或者函数声明在其他的源文件里,而不用include头文件的方式来引用该函数,在链接时,链接器在各个模块中搜索这个变量或者函数来进行最终链接。


 


[extern “C”]解析


      使用这种extern的情况多发生在使用C++调用由C写成的函数库时,此时编译过程中常发生编译器找不到C函数的问题,从而导致链接失败。为了解决这种情况,才引用了extern “C”这种用法。


那为什么又会出现这种链接失败的情况呢?简单来是由于g++和gcc生成函数名称方式的不同造成的。C++语言在编译的时候为了解决函数的多态问题,不会直接使用程序中书写的函数名称,而会使用一种特别的方法经过中间变换过程生成一个全局唯一函数名。C库函数是没有经过函数名称变换得来的,当C++使用经过变换的函数名称去调没有变换过的函数时,肯定会出现链接失败的情况。


这种特殊的转换方法叫做“名称的特殊处理(Name Mangling)”,比如C++将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern“C”进行链接指定,这告诉编译器,这是一个用C写成的库文件,请用C的方式来链接它们。


      下面我们使用一个简单的例子来说明这个问题。我们分别编译如下两段基本相同的程序,重点留意下函数”int f(void)”,在两种书写方式下的汇编语言表示方法。将源代码分别保存后,使用g++ -S test1.cpp和g++ -S test2.cpp编译生成汇编代码,对比如下,红色部分标出左右两部分不同的部分。


 



//保存该文件为:test1.cpp


#include <stdio.h>


extern "C"


{


int f(void)


{


return 1;


}


}


int main()


{


f();


return 0;


}


//保存该文件为:test2.cpp


#include <stdio.h>


 


 


int f(void)


{


return 1;


}


 


int main()


{


   f();


   return 0;


}


 



       .file  "test1.cpp"


       .text


       .align 2


.globl f


       .type  f, @function


f:


.LFB3:


       pushl  %ebp


.LCFI0:


       movl   %esp, %ebp


.LCFI1:


       movl   $1, %eax


       popl   %ebp


       ret


.LFE3:


       .size  f, .-f


       .align 2


.globl main


       .type  main, @function


main:


.LFB5:


       pushl  %ebp


.LCFI2:


       movl   %esp, %ebp


.LCFI3:


       subl   $8, %esp


.LCFI4:


       andl   $-16, %esp


       movl   $0, %eax


       subl   %eax, %esp


       call   f


       movl   $0, %eax


       leave


       ret


.LFE5:


       .size  main, .-main


       .section       .note.GNU-stack,"",@progbits


       .ident "GCC: (GNU) 3.3.4"


       .file  "test.cpp"


       .text


       .align 2


.globl _Z1fv


       .type  _Z1fv, @function


_Z1fv:


.LFB3:


       pushl  %ebp


.LCFI0:


       movl   %esp, %ebp


.LCFI1:


       movl   $1, %eax


       popl   %ebp


       ret


.LFE3:


       .size  _Z1fv, .-_Z1fv


       .align 2


.globl main


       .type  main, @function


main:


.LFB5:


       pushl  %ebp


.LCFI2:


       movl   %esp, %ebp


.LCFI3:


       subl   $8, %esp


.LCFI4:


       andl   $-16, %esp


       movl   $0, %eax


       subl   %eax, %esp


       call   _Z1fv


       movl   $0, %eax


       leave


       ret


.LFE5:


       .size  main, .-main


       .section       .note.GNU-stack,"",@progbits


       .ident "GCC: (GNU) 3.3.4"


 


      总结:带extern “C”的代码在处理函数名称时,直接使用函数的名称,不采用特别的方法生成一个中间函数名称;而带extern ”C”的代码在处理函数名称时,使用特别方法生成一个中间函数名称。在C代码中也不会生成中间函数名称,所以C++函数在使用C函数时,加上extern “C”才能正确的找到指定的函数。


 


      注:1.如果函数库没有考虑C++引用的情况,已生成库文件。我们在使用它的头文件时,可以如此使用可解决连接问题,这样时候后编译在引用cgi.h中的函数时,会按照C语言方式引用:


extern "C"{


#include "cgi.h"


}


             2.如果要写一个C函数库,我们应当尽量采用如下书写方式,而避免C++用户出现[注1]中的问题。


             



//在cgi.h文件开始处


#ifdef __cplusplus


extern "C"{


#endif /* __cplusplus */




//cgi.h文件结束的地方


#ifdef __cplusplus


}


#endif /* __cplusplus */


PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
7
关闭 站长推荐上一条 /3 下一条