• 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



  • 相关阅读:
    Git学习
    flask学习5 错误页面
    flask学习4-会话
    flask学习3
    线性代数复习
    flask学习2
    flask学习1
    windows下安装easy_install,pip,及flask入门教程链接
    MacOS Terminal调用Python代码
    [java] 类变量初始化顺序
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3013727.html
Copyright © 2020-2023  润新知