extern “C”

1、简介

extern "C"包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。

2、含义

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

extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。

extern int a;

仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。

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

与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。

(2)被extern "C"修饰的变量和函数是按照C语言方式编译和连接的;

这涉及到连接规范这个概念。在使用不同编程语言进行软件联合开发时,需要统一全局函数、全局变量、全局常量、数据类型等链接规范(Linkage Specification),特别是在不同模块之间共享的接口定义部分。这是因为链接规范关系到编译器采用什么样的Name-Mangling方案来重命名这些标示符的问题,而如果同一个标示符在不同的编译单元或模块中具有不一致的链接规范,就会产生不一致的内部名称,这肯定会导致程序连接失败。

<1> 未加extern “C”声明时的编译方式

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

void add( int x, int y );

该函数被C编译器编译后在符号库中的名字为_add,而C++编译器则会产生像_add_int_int之类的名字。

<2> 未加extern "C"声明时的连接方式

假设在C++中,A程序的头文件如下:

1 //cpp_test_a.h
2 #ifndef CPP_TEST_A_H
3 #define CPP_TEST_A_H
4 
5 int add( int x, int y );
6 
7 #endif

在B程序中引用发改函数:

1 //cpp_test_b.cpp
2 #include "cpp_test_a.h"
3 int main()
4 {
5     add(2,3);
6 }

实际上,在连接阶段,连接器会从程序A生成的目标文件cpp_test_a.o中寻找_add_int_int这样的符号!

<3> 加extern "C"声明后的编译和连接方式

加extern "C"声明后,模块A的头文件变为:

1 //cpp_test_a.h
2 #ifndef CPP_TEST_A_H
3 #define CPP_TEST_A_H
4 
5 extern "C" int add( int x, int y );
6 
7 #endif

在程序B的实现文件中仍然调用add( 2,3 ),其结果是:

a. 程序A编译生成add的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;

b. 连接器在为程序B的目标代码寻找add(2,3)调用时,寻找的是未经修改的符号名_add。

如果在程序A中函数声明了add为extern "C"类型,而程序B中包含的是extern int add( int x, int y ) ,则程序B找不到程序A中的函数;反之亦然。

所以,可以用一句话概括extern “C”这个声明的真实目的:实现C++与C及其它语言的混合编程

3、实例

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

1 extern "C" 
2 { 
3 #include "cExample.h" 
4 }

而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。

笔者编写的C++引用C函数例子工程中包含的三个文件的源代码如下:

1 //c_example.h
2 #ifndef C_EXAMPLE_H
3 #define C_EXAMPLE_H
4 
5 extern int add(int x, int y);
6 
7 #endif
1 //c_example.c
2 #include "c_example.h"
3 
4 int add(int x, int y)
5 {
6     return x+y;
7 }
1 //cpp_src.cpp
 2 extern "C"
 3 {
 4 #include "c_example.h"
 5 }
 6 #include <iostream>
 7 
 8 int main(int argc, char *argv[])
 9 {
10     int r = add(2,3);
11     std::cout<<r<<std::endl;
12     return 0;
13 }

注意:

编译命令应该为:

gcc -c c_example.o c_example.c

g++ -o cpp_src cpp_src.cpp c_example.o

若直接使用命令:

g++ -o cpp_src cpp_src.cpp c_example.c

会报编译错误。

而若

1 //cpp_src.cpp
 2 #include "c_example.h"
 3 #include <iostream>
 4 
 5 int main(int argc, char *argv[])
 6 {
 7     int r = add(2,3);
 8     std::cout<<r<<std::endl;
 9     return 0;
10 }

直接使用命令:

g++ -o cpp_src cpp_src.cpp c_example.c

可行,但是会把add函数按照c++的方式解释为_add_int_int。

如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern "C" { }。

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

笔者编写的C引用C++函数例子工程中包含的三个文件的源代码如下:

1 //cpp_example.h
2 #ifndef CPP_EXAMPLE_H
3 #define CPP_EXAMPLE_H
4 
5 extern "C" int add(int x, int y);
6 
7 #endif
1 //cpp_example.cpp
2 #include "cpp_example.h"
3 
4 int add(int x, int y)
5 {
6     return x+y;
7 }
1 //c_src.c
 2 #include <stdio.h>
 3 
 4 extern int add(int x, int y);
 5 
 6 int main()
 7 {
 8     int r = add(2,4);
 9     printf("r=%d\n", r);
10 }

注意:

编译命令应该为:

gcc -c cpp_example.o cpp_example.cpp

gcc -lstdc++ -o c_src c_src.c cpp_example.o

本文主要是自己在学习C++时的一些总结,希望对您的学习有些帮助。学习的主要参考资料:

http://www.cppblog.com/Macaulish/archive/2008/06/17/53689.html

http://baike.baidu.com/view/2816461.htm?fromId=2814224

《高质量程序设计指南C++/C语言》 林锐

原文链接: https://www.cnblogs.com/neumayue/p/3207798.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/96923

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月10日 上午3:55
下一篇 2023年2月10日 上午3:58

相关推荐