zoukankan      html  css  js  c++  java
  • 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



  • 相关阅读:
    Eclipse中常用的快捷键总结!不收藏后悔!
    MySQL基本命令整理,java数据库秘籍!
    centos 禁用ip v6
    win7 & win10 安装AD管理工具
    CentOS LVM 卷在线扩容
    Windows 与 Linux 、esxi下面查看内存容量和数量
    ESX/ESXi 主机上的每个插槽中安装了多少内存
    使用 esxcli storage vmfs unmap 命令在精简置备的 LUN 上回收 VMFS 删除的块
    vSphere 高级特性FT配置与管理
    vSphere HA 原理与配置
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3013727.html
Copyright © 2011-2022 走看看