C++中的extern “C”用法详解

    extern "C"表明了一种编译规约,其中extern是关键字属性,“C”表征了编译器链接规范。对于extern "C"可以理解成在C++/C中的混合编程的编译指令。用“C”来规约在C++中用C的方式进行编译和链接。

extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。让我们来详细解读这两重含义。

1.    被extern"C"限定的函数或变量是extern类型的;

2.    extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量是一个已经定义的外部变量或函数,有了此声明,就可以从声明处起,合法的使用该变量或函数对于extern变量来说,仅仅是一个变量的声明,其并不是在定义分配内存空间。记住,下列语句:extern int a;仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间类型名int可以省略不写变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。

通常,也可以在模块A的头文件中对本模块A提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。而且编译阶段,外面是找不到该函数的,但是不报错。link阶段会从定义模块生成的目标代码中找到此函数。

与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。

作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。从代码到可执行程序需要经过编译和链接两个过程,其中编译阶段会做语法检测,代码展开,另外它还会做一件事,就是将变量转成符号,链接的时候其实是通过符号来定位的。编译器在编译C和C++代码时,将变量转成符号的过程符号名称是不同的,混合编程在链接时就会出现找不到函数的现象。例如,假设某个函数的原型为:

void foo( int x, int y );

该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangledname”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y)编译生成的符号是不相同的,后者为_foo_int_float。

这样也很容易理解为什么C++支持函数重载而C不支持了,因为C++将函数修饰为符号时把函数的参数类型加进去了,而C却没有,所以在C++下,即便函数名相同,只要参数不同,它们的符号名是不会冲突的

(1)在C++中引用C语言中的函数,需要在C++头文件里加extern “C”,否则C++编译的时候会找不到符号

/* c语言头文件:cExample.h */

1
2
3
4
#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H
extern int add(int x,int y);    //对其外部函数指定为extern
#endif

/* c语言实现文件:cExample.c */

1
2
3
4
5
#include "cExample.h"
int add( int x, int y )
{
   return x + y;
}

// c++实现文件,调用add:cppFile.cpp

1
2
3
4
5
6
7
8
9
extern "C"  //采用C编译器编译的C语言代码段
{
   #include "cExample.c" 
}
int main(int argc, char* argv[])
{
   add(2,3);
   return 0;
}

 

(2)在C中引用C++语言中的函数,C++的头文件需添加extern "C",但是在C语言中不能直接引用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C"函数声明为extern类型。

1. 在C++的.h文件中用extern “C”{}声明将被C程序使用的函数

//C++头文件 cppExample.h

1
2
3
4
#ifndef CPP_EXAMPLE_H
#define CPP_EXAMPLE_H
extern "C" int add( int x, int y );  //采用C编译器编译的C语言代码段
#endif

2. 在C++的.cpp文件中实现上面的函数

//C++实现文件 cppExample.cpp

1
2
3
4
5
#include "cppExample.h"
int add( int x, int y )
{
    return x + y;
}

3. 在.c文件中用extern声明要使用的C++函数

// C实现文件 cFile.c    注意:切不可在.c文件中包含C++的.h文件,那样编译无法通过

/* 这样会编译出错:#include "cppExample.h" */

1
2
3
4
5
6
extern int add( int x, int y );    //仅将C文件中将C++中定义的extern "C"函数声明为extern类型
int main( int argc, char* argv[] )
{
add( 2, 3 );
return 0;
}

 

extern "C"——用“C”来规约在C++中用C的方式进行编译和链接的更多相关文章

  1. C、C++编译,链接,extern链接

    //b.cpp #inlcude <iostream> void b() { std::cout<<"fun b"; } //a.cpp extern vo ...

  2. extern c

    extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码.加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C+ ...

  3. extern C的作用详解

    extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码.加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C+ ...

  4. extern “C”的作用详解

    extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码.加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C+ ...

  5. C++之 extern C的作用详解

    extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码.加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C+ ...

  6. const与#define、结构体对齐、函数重载name mangling、new/delete 等

    一.bool 类型 逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC++中为1个字节. 声明方式:bool result; result ...

  7. C# 调用 C++ dll的两种方式

    目录: 1.非托管方式 2.托管方式 3.介绍 extern "C" 4.介绍   DllImport 1.非托管方式 第一种,非托管方式:调用类和方法https://www.co ...

  8. C++ 调用C语言、extern "C"、__cplusplus关键字

    ——C++编译器完全兼容C语言的编译方式.(但是得有源代码) ——C++编译器会优先使用C++的编译方式进行编译 ——extern "C" 关键字能够强制C++编译器进行C方式的编 ...

  9. Jni的一个异常

    UnsatisfiedLinkError:No implementation found for java.lang.String com.skymaster.hs.test4.MainActivit ...

随机推荐

  1. fiddler 记录一些以前不熟悉的东西

    fidddler已经用了3年了,一般就是抓包,看基本的信息,然后就是本地文件替换网络文件. 然后有一些很有用的东西的却没有深入的学习过.  -_-!. 抓https的包 fiddler的过滤器的使用 ...

  2. 跟我一起学习ASP.NET 4.5 MVC4.0(四)(转)

    前几个文章中介绍了一些关于MVC4.0的东东,今天我们来看一下登陆验证,也可以说是权限验证,即AuthorizeAttribute.这个可以使用在控制器Controller上,也可以使用在Action ...

  3. Part 3 talking about constraint in sql

    What is Foreign key and how to create a Foreign key constraint? Note:Foreign Keys are used to enforc ...

  4. Android SurfaceView 绘图覆盖刷新及脏矩形刷新方法

    http://www.cnblogs.com/SkyD/archive/2010/11/08/1871423.html Android SurfaceView 绘图覆盖刷新及脏矩形刷新方法 Surfa ...

  5. C#实现自定义事件,用于监视变量变化

    很多时候我们需要程序具有一种功能,就是当满足某一条件时触发某个动作,使用C#的事件机制就可以达到这个目的下面的例子是一个很好的演示. 这段代码实现了对一个变量的监视,一旦变量发生改变,就触发动作 定义 ...

  6. Elementary os的安装

      1.         使用安装文件进行数据读取 2.         进入安装界面 3.         选择语言并进行安装(可以先试用) 4.         选择继续(可以勾选两个选项,意思是 ...

  7. 集合框架学习之Guava Collection

    开源工具包: Guava : Google Collection Apache:Commons Collecton 1.1 Google Collections Guava:google的工程师利用传 ...

  8. OC8_代理基本概念

    // // ProtectedDelegate.h // OC8_代理基本概念 // // Created by zhangxueming on 15/6/24. // Copyright (c) 2 ...

  9. Coarse-Grained lock 粗粒度锁

    用一个锁Lock一组相关的对象 有时,需要按组来修改多个对象. 这样,在需要锁住其中一个的时候,必须连带地将其他的对象都上锁. 为每一个对象都加上一个锁是很繁琐的. 粗粒度锁是覆盖多个对象的单个锁. ...

  10. [Bootstrap]组件(一)

    Glyphicons字体图标 基类.glyphicon  {position/top/display/font-family/} 具体类  {content}  --表现在伪元素上 使用要点:a.基类 ...