extern "C" 学习笔记

本文介绍了C++中的extern "C"的用途和原理,解释了由于C++支持函数重载而C语言不支持导致的名称修饰问题。通过extern "C",可以在C++中正确引用C语言的函数和变量,详细阐述了extern "C"的惯用法,包括在包含C头文件时如何处理和在无法修改C头文件时如何在C++中调用C代码。
摘要由CSDN通过智能技术生成

1.引言

有一道经典的程序员面试题,如下:


//为什么标准头文件都有和以下 相似的结构?
#ifndef DuNiangHeadFile
#define DuNiangHeadFile
 

#ifdef __cplusplus
extern "C" {
#endif 

/*bla bla bla*/ 

#ifdef __cplusplus
}
#endif 

#endif /* DuNiangHeadFile */

对于头文件中的编译宏的作用,显然是为了防止该头文件被重复引用。

#ifndef DuNiangHeadFile
#define DuNiangHeadFile
 

#endif /* DuNiangHeadFile */


那么剩下代码的作用又是什么呢?

#ifdef __cplusplus
extern "C" {
#endif 

/*bla bla bla*/ 

#ifdef __cplusplus
}
#endif 

这正是本篇博文所要阐述的内容。下面我们进入正题。


2.揭密extern "C"

从直观上来讲,extern "C" 显然有两层含义。其一,是 被它修饰的目标是“extern”,即该目标具有外部链接性,可以在其他编译单元(文件)中被引用。其二,被它修饰的目标是“C”类型的,即编译器或链接器要按照C的编译规则来对其进行编译或链接。

之所以采用这种方式,是因为C和C++这两种语言之间的一些差异导致的。

作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:

int myFunc(int a, int b);

经过C++编译器编译之后 ,在其目标文件.o 文件中,有一个_Z10myFuncii 符号,这个符号就代表了源文件中的int myFunc(int a, int b)函数了。不同的编译器可能生成不同的名字,但是都采用了相同的机制,即C++ Primer中所说的“名字粉碎”(name mangling)或“名字修饰”(name decoration)。

_Z10myFuncii 这样的名字包含了函数名、函数参数数量及类型信息,其最后的两个字符i 就表示第一参数和第二参数都是整型。C++就是靠这种机制来实现函数重载的。在链接阶段,链接器就会从生成的 .o目标文件中寻找_Z10myFuncii 这样的符号,从而生成可执行文件。

而对于加extern "C"声明后,即假设某个函数的原型为:

#ifdef __cplusplus
extern "C" {
#endif

    int myFunc(int a, int b);

#ifdef __cplusplus
}
#endif 

编译生成myFunc函数的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;链接器在为其他的目标代码寻找myFunc函数调用时,寻找的是未经修改的符号名_myFunc。正是因为如此,才实现了C++与C及其它语言的混合编程。


3.extern "C"的惯用法

明白了C++中extern "C"的基本原理,下面我们来具体分析一下extern "C"的常用技巧。

(1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:

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

而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在C文件的头文件中直接extern "C"时会出现编译语法错误。但是,说的是直接使用会出错。如果略施小计,C语言头文件中也是可以使用extern "C"的。

用g++编译cpp程序时,编译器会定义宏 __cplusplus ,可根据__cplusplus是否已经定义,来决定是否需要extern "C"。

#ifdef __cpluscplus  
extern "C" {  
#endif  
  
//正常C语言头文件 
  
#ifdef __cplusplus  
}  
#endif  

这是你可以修改C语言.h文件的情况。

(2)当你不能修改C语言.h文件,比如这个头文件是公司里的前辈写的,或者,公司规定禁止修改没有BUG的历史遗留代码,以防引入不必要的新Bug。

同时,.h文件中没有extern "C"关键字,而你的C++程序又要链接使用由C编译好的.o目标文件。这时该怎么办呢?

可以这样,在你的c++文件中,包含该模块的头文件时加上extern "C", 如下:

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

如果仅仅使用其中的一个函数,而不需要include整个头文件时,也可以单独声明该函数,如下:

extern "C" {  
int myFunc(int , int);
}  

这样,C++编译器在编译,或链接器在链接的时候,就会以C风格在目标文件中生成或寻找目标符号了。

同理,如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern "C" { }。这突然让我想到了com组件技术。大笑





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值