extern的用法小结

 

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.cppg++ -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 */

 

 

 

 

posted on 2008-04-09 22:14  kevin.Tian  阅读(8102)  评论(0编辑  收藏  举报

导航