原文:http://bbs.ednchina.com/BLOG_ARTICLE_251752.HTM

时常在cpp的代码之中看到这样的代码:

    
#ifdef
 __cplusplus

extern "C" { 
    #endif

//一段代码

    
#ifdef
 __cplusplus


#endif 
  这样的代码到底是什么意思呢?首先,__cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码,也就是说,上面的代码的含义是:如果这是一段cpp的代码,那么加入extern 
"C"{和}处理其中的代码。

  要明白为何使用extern 
"C",还得从cpp中对函数的重载处理开始说起。在c++中,为了支持重载机制,在编译生成的汇编码中,要对函数的名字进行一些处理,加入比如函数的返回类型等等.而在C中,只是简单的函数名字而已,不会加入其他的信息.也就是说:C++和C对产生的函数名字的处理是不一样的.

  比如下面的一段简单的函数,我们看看加入和不加入extern "C"产生的汇编代码都有哪些变化:

int f(void) 
    { 
        return 1; 
    } 
  在加入extern 
"C"的时候产生的汇编代码是:

.file "test.cxx" 
    .text 
    .align 2 
    .globl _f 
    
.def _f; .scl 2; .type 32; .endef 
    _f: 
        pushl %ebp 
        
movl %esp, %ebp 
        movl $1, %eax 
        popl %ebp 
        ret

  但是不加入了extern "C"之后

.file "test.cxx" 
    .text 
    .align 2 
    .globl __Z1fv

.def __Z1fv; .scl 2; .type 32; .endef 
    __Z1fv: 
        pushl 
%ebp 
        movl %esp, %ebp 
        movl $1, %eax 
        popl %ebp

ret 
  两段汇编代码同样都是使用gcc 
-S命令产生的,所有的地方都是一样的,唯独是产生的函数名,一个是_f,一个是__Z1fv。

  明白了加入与不加入extern "C"之后对函数名称产生的影响,我们继续我们的讨论:为什么需要使用extern 
"C"呢?C++之父在设计C++之时,考虑到当时已经存在了大量的C代码,为了支持原来的C代码和已经写好C库,需要在C++中尽可能的支持C,而 extern 
"C"就是其中的一个策略。

  试想这样的情况:一个库文件已经用C写好了而且运行得很良好,这个时候我们需要使用这个库文件,但是我们需要使用C++来写这个新的代码。如果这个代码使用的是C++的方式链接这个C库文件的话,那么就会出现链接错误.我们来看一段代码:首先,我们使用C的处理方式来写一个函数,也就是说假设这个函数当时是用C写成的:

//f1.c 
    extern "C" 
    { 
        void f1() 
        {

return; 
        } 
    } 
  编译命令是:gcc -c f1.c -o f1.o 
产生了一个叫f1.o的库文件。再写一段代码调用这个f1函数:

// test.cxx 
//这个extern表示f1函数在别的地方定义,这样可以通过 
//编译,但是链接的时候还是需要

//链接上原来的库文件. 
    extern void f1();

int main() 
    { 
        f1();

return 0; 
    } 
  通过gcc -c test.cxx -o test.o 
产生一个叫test.o的文件。然后,我们使用gcc test.o f1.o来链接两个文件,可是出错了,错误的提示是:

test.o(.text + 0x1f):test.cxx: undefine reference to 'f1()'

  也就是说,在编译test.cxx的时候编译器是使用C++的方式来处理f1()函数的,但是实际上链接的库文件却是用C的方式来处理函数的,所以就会出现链接过不去的错误:因为链接器找不到函数。

  因此,为了在C++代码中调用用C写成的库文件,就需要用extern "C"来告诉编译器:这是一个用C写成的库文件,请用C的方式来链接它们。

  比如,现在我们有了一个C库文件,它的头文件是f.h,产生的lib文件是f.lib,那么我们如果要在C++中使用这个库文件,我们需要这样写:

extern "C" 
    { 
        #include "f.h" 
    }

  回到上面的问题,如果要改正链接错误,我们需要这样子改写test.cxx:

extern "C" 
    { 
        extern void f1(); 
    }

int main() 
    { 
        f1();

return 0; 
    } 
  重新编译并且链接就可以过去了.

  总结

  C和C++对函数的处理方式是不同的.extern 
"C"是使C++能够调用C写作的库文件的一个手段,如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern "C"来说明。

【转】#ifdef __cplusplus深度剖析的更多相关文章

  1. libevent学习笔记(参考libevent深度剖析)

    最近自学libevent事件驱动库,参考的资料为libevent2.2版本以及张亮提供的<Libevent源码深度剖析>, 参考资料: http://blog.csdn.net/spark ...

  2. libevent源码深度剖析十

    libevent源码深度剖析十 ——支持I/O多路复用技术 张亮 Libevent的核心是事件驱动.同步非阻塞,为了达到这一目标,必须采用系统提供的I/O多路复用技术,而这些在Windows.Linu ...

  3. libevent源码深度剖析八

    libevent源码深度剖析八 ——集成信号处理 张亮 现在我们已经了解了libevent的基本框架:事件管理框架和事件主循环.上节提到了libevent中I/O事件和Signal以及Timer事件的 ...

  4. 《AngularJS深度剖析与最佳实践》简介

    由于年末将至,前阵子一直忙于工作的事务,不得已暂停了微信订阅号的更新,我将会在后续的时间里尽快的继续为大家推送更多的博文.毕竟一个人的力量微薄,精力有限,希望大家能理解,仍然能一如既往的关注和支持sh ...

  5. ASP.NET Core管道深度剖析(2):创建一个“迷你版”的管道来模拟真实管道请求处理流程

    从<ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求>我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但 ...

  6. Objective-C类成员变量深度剖析

    目录 Non Fragile ivars 为什么Non Fragile ivars很关键 如何寻址类成员变量 真正的“如何寻址类成员变量” Non Fragile ivars布局调整 为什么Objec ...

  7. #ifdef __cplusplus extern "C" { #endif

    1.在好多程序中我们会遇到下面代码段 #ifdef __cplusplus        extern "C" {        #endif //c语法代码段 #ifdef __ ...

  8. #ifdef __cplusplus extern "C"

    #ifdef __cplusplus extern "C" { #endif //一段代码 #ifdef __cplusplus } #endif首先,__cplusplus是cp ...

  9. 大众点评开源分布式监控平台 CAT 深度剖析

    一.CAT介绍 CAT系统原型和理念来源于eBay的CAL的系统,CAT系统第一代设计者吴其敏在eBay工作长达十几年,对CAL系统有深刻的理解.CAT不仅增强了CAL系统核心模型,还添加了更丰富的报 ...

随机推荐

  1. 移动终端学习2:触屏原生js事件及重力感应

    如今智能移动设备已经渗透到人们生活的方方面面,用户数量也在不断迅速增长(市场研究机构 eMarketer 在今年初发表的趋势报告中预测,2014年至2018年,中国智能手机用户从总人口的 38.3%增 ...

  2. HDU 1043 Eight (A* + HASH + 康托展开)

    Eight Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Sub ...

  3. Oracle_11g中解决被锁定的scott用户的方法(转载)

    转自:http://www.2cto.com/database/201402/277206.html Oracle 11g中修改被锁定的用户:scott 在安装完Oracle11g和创建完oracle ...

  4. LeetCode 260

    Single Number III Given an array of numbers nums, in which exactly two elements appear only once and ...

  5. VM虚拟机下centos7 无法上网的问题解决办法

    博主本着学无止境的精神在虚拟机上安装了一个centos7 来敲敲命令行.刚开始就遇到了强大的阻力... ifconfig   vim  都没法用.这怎么行,安装呗.又学了圈安装,yum命令. 结果yu ...

  6. jquery点击控制动画暂停开始

    一下是从w3c上面考下来了的, animation:[[ animation-name ] || [ animation-duration ] || [ animation-timing-functi ...

  7. .NET 认识

  8. Spring(3.2.3) - Beans(12): 属性占位符

    使用属性占位符可以将 Spring 配置文件中的部分元数据放在属性文件中设置,这样可以将相似的配置(如 JDBC 的参数配置)放在特定的属性文件中,如果只需要修改这部分配置,则无需修改 Spring ...

  9. Ehcache(2.9.x) - Configuration Guide, Configuring Storage Tiers

    About Storage Tiers Ehcache has three storage tiers, summarized here: Memory store – Heap memory tha ...

  10. Linux 命令 - killall: 通过进程名向进程发送信号

    命令格式 killall [-Z CONTEXT] [-u USER] [ -eIgiqrvw ] [ -SIGNAL ] NAME... killall -l, --list killall -V, ...