[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编译生成汇编代码,对比如下,红色部分标出左右两部分不同的部分。
1 | //保存该文件为:test1.cpp | 2 | #include <stdio.h> | 3 | extern "C" | 4 | { | 5 | int f(void) | 6 | { | 7 | return 1; | 8 | } | 9 | } | 10 | int main() | 11 | { | 12 | f(); | 13 | return 0; | 14 | } | 15 | | | 1 | //保存该文件为:test2.cpp | 2 | #include <stdio.h> | 3 | | 4 | | 5 | int f(void) | 6 | { | 7 | return 1; | 8 | } | 9 | | 10 | int main() | 11 | { | 12 | f(); | 13 | return 0; | 14 | } | 15 | | |
1 | .file "test1.cpp" | 2 | .text | 3 | .align 2 | 4 | .globl f | 5 | .type f, @function | 6 | f: | 7 | .LFB3: | 8 | pushl %ebp | 9 | .LCFI0: | 10 | movl %esp, %ebp | 11 | .LCFI1: | 12 | movl $1, %eax | 13 | popl %ebp | 14 | ret | 15 | .LFE3: | 16 | .size f, .-f | 17 | .align 2 | 18 | .globl main | 19 | .type main, @function | 20 | main: | 21 | .LFB5: | 22 | pushl %ebp | 23 | .LCFI2: | 24 | movl %esp, %ebp | 25 | .LCFI3: | 26 | subl $8, %esp | 27 | .LCFI4: | 28 | andl $-16, %esp | 29 | movl $0, %eax | 30 | subl %eax, %esp | 31 | call f | 32 | movl $0, %eax | 33 | leave | 34 | ret | 35 | .LFE5: | 36 | .size main, .-main | 37 | .section .note.GNU-stack,"",@progbits | 38 | .ident "GCC: (GNU) 3.3.4" | 39 | | | 1 | .file "test2.cpp" | 2 | .text | 3 | .align 2 | 4 | .globl _Z1fv | 5 | .type _Z1fv, @function | 6 | _Z1fv: | 7 | .LFB3: | 8 | pushl %ebp | 9 | .LCFI0: | 10 | movl %esp, %ebp | 11 | .LCFI1: | 12 | movl $1, %eax | 13 | popl %ebp | 14 | ret | 15 | .LFE3: | 16 | .size _Z1fv, .-_Z1fv | 17 | .align 2 | 18 | .globl main | 19 | .type main, @function | 20 | main: | 21 | .LFB5: | 22 | pushl %ebp | 23 | .LCFI2: | 24 | movl %esp, %ebp | 25 | .LCFI3: | 26 | subl $8, %esp | 27 | .LCFI4: | 28 | andl $-16, %esp | 29 | movl $0, %eax | 30 | subl %eax, %esp | 31 | call _Z1fv | 32 | movl $0, %eax | 33 | leave | 34 | ret | 35 | .LFE5: | 36 | .size main, .-main | 37 | .section .note.GNU-stack,"",@progbits | 38 | .ident "GCC: (GNU) 3.3.4" | 39 | | |