参考:
http://www.cnblogs.com/rollenholt/archive/2012/03/20/2409046.html
被extern "C"修饰的变量和函数是按照C语言方式编译和连接的,实现C++与C的混合编程。
下面做几个实验来理解:
C++和C编译时对函数名的处理
下面是一个C++文件, a.cpp
void func1(void) {} void func2(int a) {} void func3(int a, int b) {} int func4(void) {} int func5(int a) {} int func6(int a, int b) {} int main(int argc, const char *argv[]) { func1(); func2(1); func3(1, 2); func4(); func5(1); func6(1, 2); return 0; }
使用命令 g++ -S a.cpp 编译这个文件,得到编译后得到的汇编文件 a.s,摘录部分内容如下:
1 .LFB1026: 2 .cfi_startproc 3 pushq %rbp 4 .cfi_def_cfa_offset 16 5 .cfi_offset 6, -16 6 movq %rsp, %rbp 7 .cfi_def_cfa_register 6 8 subq $16, %rsp 9 movl %edi, -4(%rbp) 10 movq %rsi, -16(%rbp) 11 call _Z5func1v 12 movl $1, %edi 13 call _Z5func2i 14 movl $2, %esi 15 movl $1, %edi 16 call _Z5func3ii 17 call _Z5func4v 18 movl $1, %edi 19 call _Z5func5i 20 movl $2, %esi 21 movl $1, %edi 22 call _Z5func6ii 23 movl $0, %eax 24 leave 25 .cfi_def_cfa 7, 8 26 ret 27 .cfi_endproc 28 .LFE1026:
可以看到,将函数名分别进行了如下处理:
void func1(void) ---> _Z5func1v void func2(int a) ---> _Z5func2i void func3(int a, int b) ---> _Z5func3ii int func4(void) ---> _Z5func4v int func5(int a) ---> _Z5func5i int func6(int a, int b) ---> _Z5func6ii
可以看到,将来参数类型会和函数名结合,从而产生新的标示,与返回值的类型没有关系。这就解释了C++允许函数重载的原因,并且知道了为什么仅返回值类型不同不能认为是重载的原因。
还是这个文件,我们重命名为 b.c,然后使用命令 gcc -S b.c,得到汇编文件 b.s,摘录部分内容如下:
main: .LFB6: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movl %edi, -4(%rbp) movq %rsi, -16(%rbp) call func1 movl $1, %edi call func2 movl $2, %esi movl $1, %edi call func3 call func4 movl $1, %edi call func5 movl $2, %esi movl $1, %edi call func6 movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc
可以看到,在编译过程中,函数的名称并没有改变,这也就解释了C语言为什么不支持函数的重载。
那么,既然C和C++在编译时对函数名的处理不同,那么如何实现C和C++的混合编程呢?即在C中调用C++的函数,在C++中调用C的函数。
先看一下现象:
a.cpp
int main(int argc, const char *argv[]) { func1(); return 0; }
使用命令 g++ -c a.cpp,出现如下错误:
a.cpp: In function ‘int main(int, const char**)’: a.cpp:3:8: error: ‘func1’ was not declared in this scope func1(); ^
原因是我们没有声明func1这个函数,我发现,如果是在C语言中,不声明并不会导致编译错误,原因其实很简单,在C++中,编译的时候给函数设置新的标示符的时候要用到函数的参数类型,如果不声明,编译器无法获得func1的参数类型信息,从而编译出错。而在C语言中,编译过程中函数的标识符就是函数名,不考虑参数信息,也就不会出错。
修改a.cpp如下:
extern func1(void); int main(int argc, const char *argv[]) { func1(); return 0; }
相应的汇编部分内容如下:
main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movl %edi, -4(%rbp) movq %rsi, -16(%rbp) call _Z5func1v movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0:
b.c
void func1(void) {}
使用如下命令编译: gcc -c b.c
相应的汇编文件如下:
.file "b.c" .text .globl func1 .type func1, @function func1: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0:
然后使用命令 g++ a.o b.o 链接,提示如下错误:
a.o: In function `main‘: a.cpp:(.text+0x10): undefined reference to `func1()‘ collect2: error: ld returned 1 exit status
原因是, 在链接时,由于找不到_Z5func1v导致编译失败,解决办法如下:
修改a.cpp
extern "C" void func1(void); int main(int argc, const char *argv[]) { func1(); return 0; }
我们看一下得到的汇编文件(g++ -S a.cpp):
main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movl %edi, -4(%rbp) movq %rsi, -16(%rbp) call func1 movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0:
可以看到,标识符已经从_Z5func1v变成了func1,即从C++的编译方式变成了C的编译方式,这样在链接时就不会出错了。
那么如果是在C语言中调用C++的函数,结果如何呢?
a.c
int main(int argc, const char *argv[]) { func1(); return 0; }
使用命令 gcc -c a.c编译得到a.o
得到的汇编文件如下:
main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movl %edi, -4(%rbp) movq %rsi, -16(%rbp) movl $0, %eax call func1 movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0:
b.cpp
void func1(void) {}
得到的汇编文件如下:
.file "b.cpp" .text .globl _Z5func1v .type _Z5func1v, @function _Z5func1v: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0:
使用命令 g++ -c b.cpp编译得到b.o
使用命令gcc a.o b.o出现如下错误的提示
a.o: In function `main‘: a.cpp:(.text+0x10): undefined reference to `func1‘ collect2: error: ld returned 1 exit status
修改b.cpp
extern "C" void func1(void) {}
注:这里不能修改a.c,因为C语言中无法识别extern "C"
得到的汇编如下(g++ -S b.cpp):
.file "b.cpp" .text .globl func1 .type func1, @function func1: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0:
这样在链接时就不会出错了。