C++_4——关键字(extern)
C++_4——关键字(extern)
这一系列文章的目的是在学习了C++基础后,继续补充一些C++基础和进阶的知识点,包括C++11的相关内容。
C++网站:http://www.cplusplus.com/reference/
1 extern “C” 与 C/C++混合编程
C++与C混合编程的问题
-
为什么不直接用C编译器编译C++代码
因为,C++有很多内容是C没有的,比如一些很多关键字…
另外,编译器将函数名编译成各种符号供链接器使用,C++编译器编译出来的符号是与形参列表相关的,因而重载的函数其编译出来的符号是不同的;而,C编译器编译出来的符号仅与函数名有关,因而重载的函数器编译出来的符号是相同的,这在链接时就会报错 -
为什么不直接使用C++编译器编译C代码
虽然一般意义上讲C++是C的超集,但C++与C并不是完全包含的关系,C中也有C++不支持的内容,如这篇博提及的各种差异…
另外,假设…
有C代码文件 a.c 和 a.h,并用C编译器生成目标文件 a.o;
有C++代码文件 b.cpp,并引用C头文件(a.h)和使用其中的函数,使用C++编译器生成目标文件 b.o;
链接时,需要链接 a.o 和 b.o ,这将在 b.o 产生符号未定义的错误,原因是两个目标文件对C函数编译后的符号是不同的 -
不同语言的代码尽量使用其对应的编译器编译。C++制定了链接规范,来指定编译器,比如,当需要在C++编译器中编译C代码时,使用extern “C” 调用C编译器
C++链接规范 extern"language string"
-
常用有两个:extern “C” 和 extern “C++” ,当只有extern修饰函数时,默认为extern “C++”
用法1单个声明:extern "C" void foo();
用法2一组声明:extern "C" { void foo(); int bar(); }
-
当然,加了 extern"C" 的C代码,不能直接用C编译器编译,因为C编译器不认识,需要使用条件编译,通过宏定义 __cplusplus 来判断是否使用extern"C"
-
extern"language string" 允许嵌套,跟作用域类似,程序按照所属最内层的规范编译,但尽量少嵌套,也就是把包含头文件放到extern之外
编译器 gcc和g++
- GCC,gcc,g++
GCC:GNU Compiler Collection(GUN 编译器集合),它可以编译C、C++、JAV、Fortran、Pascal、Object-C、Ada等语言
gcc是GCC中的GUN C Compiler(C 编译器)
g++是GCC中的GUN C++ Compiler(C++编译器)- gcc可以完成预处理、编译和汇编三个阶段,g++调用gcc完成前三个阶段,并自己完成库文件链接阶段
- gcc将.c和.cpp文件分别当做c和cpp文件编译,g++全部视为cpp文件编译
- 对于宏__cplusplus,当使用gcc编译cpp文件和使用g++编译c和cpp文件时会定义
- 对于extern"C",这与使用gcc还是g++编译无关
————————————————
版权声明:本文为CSDN博主「wsqyouth」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013457167/article/details/80222557
条件编译的几个使用场景
编译预处理包括头文件展开、宏替换、条件编译、去掉注释等。
- 检查宏是否被定义,避免重复包含头文件导致的重复定义
#ifndef THIS_HEADER_FILE #define THIS_HEADER_FILE /* TODO */ #endif #ifdef THIS_HEADER_FILE /* TODO */ #endif
- 判断编译环境,选择合适的编译器编译代码,如下,当C++编译器编译时,TODO 部分使用C编译器编译,其中,__cplusplus 是C++编译器的保留宏定义
#ifdef __cplusplus extern "C" { #endif /* TODO */ #ifdef __cplusplus } #endif
- 都是判断一个宏被定义的情况,每个宏都有其存在的意义和作用
2 extern 修饰变量 / 函数
修饰变量
- C++ 提供两种变量声明方式:defining declaration 和 referencing declaration,前者简称定义(definition),后者简称声明(declaration),定义会分配内存空间,声明不分配
- 若要在多个文件共享全局变量,需要在其中一个文件内包含该变量的定义,其他文件用extern修饰该变量但不初始化,如在a.cpp中定义变量m,在b.cpp中声明变量m
// a.cpp int m;
// b.cpp extern int m;
- 需要注意的是以下均为定义,即使加了extern,所以,如果多个文件用extern声明同一变量的同时初始化,就会出现重复定义的错误
// 定义变量 int m; // 即使没有初始化,也分配了内存空间,默认值为0 int m = 0; extern int m = 0;
- 还需要注意的是,用extern声明变量的语句也可以在函数体内,并非一定在函数外
- 尽量不要在头文件中定义变量,避免重复包含头文件导致的重定义,避免链接导致的重定义
- 不要过分依赖使用全局变量;对于常量数据,加const以防误改
修饰函数
- 默认函数的链接性是外部的,也就是其他文件可见,extern关键字可加可不加
- 常用的方式是将函数设为内部可见,即用static修饰函数,如下,同时给函数的声明和实现添加static,这时其他文件可以定义同名函数
static int fun(); static int fun(){}
3 其他的一些点
存储持续性和链接性
- C++存储持续性类型包括自动存储持续性(如函数内定义的变量)、静态存储持续性(函数外定义,或函数内+static定义的变量)、动态存储持续性(new和delete)、线程存储持续性(C++11,thread_local)
- 链接性:外部链接性(不同文件间共享)、内部链接性(文件内函数共享)、无链接性(代码块中共享)
- 关于extern修饰变量和函数,主要讨论的即是静态存储持续性和外部链接性
参考
- 【C++】函数重载
- C++ 是C 的超集吗?可以用C++ 编译器来编译C 代码吗?
- C/C++兼容性
- 关于extern “C”(详细剖析)
- C++ primer plus sixth edition