extern "C"的理解
extern "C"的引入是为了解决C++函数重载的问题,C++之父在设计C++语言的时候,考虑到对C的兼容,引入了extern "C",使得在C++中能够无误地使用C的库函数(大部分的库函数都是由C编写的)
在编译期间,C和C++为函数生成修饰名的方式是不一样的,这一点可以认为是C++实现函数重载的机制,考虑这样一段C代码:
int fun(int x) { return 0; }
使用 /FAs 选项生成汇编代码,留意fun函数的修饰名:
; 这是使用C编译方式进行编译的 PUBLIC _fun ; _fun是函数fun的修饰名 _TEXT SEGMENT _x$ = 8 _fun PROC NEAR ; 3 : { push ebp mov ebp, esp ; 4 : return x; mov eax, DWORD PTR _x$[ebp] ; 5 : } pop ebp ret 0 _fun ENDP _TEXT ENDS END
同样的代码,换成C++的编译方式,汇编代码如下:
PUBLIC ?fun@@YAHH@Z _TEXT SEGMENT ?fun@@YAHH@Z PROC NEAR ; ?fun@@YAHH@Z 是 fun 函数的修饰名 ; 12 : { push ebp mov ebp, esp ; 13 : return 0; xor eax, eax ; 14 : } pop ebp ret 0 ?fun@@YAHH@Z ENDP ; fun _TEXT ENDS END
可见,同样的一段代码,C和C++编译方式区别还是很大的,特别是为函数生成修饰名的时候,因此这样的程序是有问题的:
/* fun.c */ int fun(int x) { return x; }
// test.cpp #include <stdio.h> extern int fun(int); int main(void) { printf("%d\n", fun(2)); getchar(); return 0; }
提示的错误是:
原因是:fun是采用C的编译方式,编译器为其生成的函数修饰名是_fun,而在test.cpp文件中的fun函数采用的是C++编译方式,生成的修饰名是?fun@@YAHH@Z,在链接阶段,由于前后fun生成的修饰名不一致,导致重定向失败,所以就出错了!
而为了在C++中使用C编译方式,才引入了extern "C"技术(其实不光是这样,想想在项目中使用的库函数,大部分都是用C语言编译方式的),现在对上面的test.cpp代码进行改动:
// test.cpp #include <stdio.h> extern "C" { extern int fun(int); } int main(void) { printf("%d\n", fun(2)); getchar(); return 0; }
重新编译,链接,程序运行正常了!
在extern int fun(int)外加入extern "C"进行声明,就告诉编译器fun函数是按C语言编译方式进行编译的,于是,编译器就为fun函数生成C方式的修饰名,对test.cpp使用/FAs选项,留意一下fun函数的修饰名:
EXTRN _fun:NEAR _DATA SEGMENT $SG529 DB '%d', 0aH, 00H _DATA ENDS _TEXT SEGMENT _main PROC NEAR ; 10 : { push ebp mov ebp, esp push ecx ; 11 : printf("%d\n", fun(2)); push 2 call _fun ; 现在fun函数的修饰名变成C方式了! add esp, 4 push eax push OFFSET FLAT:$SG529 call _printf add esp, 8