C++_4——关键字(extern)

  这一系列文章的目的是在学习了C++基础后,继续补充一些C++基础和进阶的知识点,包括C++11的相关内容。

C++网站:http://www.cplusplus.com/reference/

1 extern “C” 与 C/C++混合编程
C++与C混合编程的问题
  1. 为什么不直接用C编译器编译C++代码
    因为,C++有很多内容是C没有的,比如一些 很多关键字…
    另外,编译器将函数名编译成各种符号供链接器使用,C++编译器编译出来的符号是与形参列表相关的,因而重载的函数其编译出来的符号是不同的;而,C编译器编译出来的符号仅与函数名有关,因而重载的函数器编译出来的符号是相同的,这在链接时就会报错

  2. 为什么不直接使用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函数编译后的符号是不同的

  3. 不同语言的代码尽量使用其对应的编译器编译。C++制定了链接规范,来指定编译器,比如,当需要在C++编译器中编译C代码时,使用extern “C” 调用C编译器

C++链接规范 extern"language string"
  1. 常用有两个:extern “C” 和 extern “C++” ,当只有extern修饰函数时,默认为extern “C++”
    用法1单个声明:extern "C" void foo();
    用法2一组声明:

    extern "C"
    {
      void foo();
      int bar();
    }
    
  2. 当然,加了 extern"C" 的C代码,不能直接用C编译器编译,因为C编译器不认识,需要使用条件编译,通过宏定义 __cplusplus 来判断是否使用extern"C"

  3. extern"language string" 允许嵌套,跟作用域类似,程序按照所属最内层的规范编译,但尽量少嵌套,也就是把包含头文件放到extern之外

编译器 gcc和g++

gcc与g++的区别

  1. 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++编译器)
  2. gcc可以完成预处理、编译和汇编三个阶段,g++调用gcc完成前三个阶段,并自己完成库文件链接阶段
  3. gcc将.c和.cpp文件分别当做c和cpp文件编译,g++全部视为cpp文件编译
  4. 对于宏__cplusplus,当使用gcc编译cpp文件和使用g++编译c和cpp文件时会定义
  5. 对于extern"C",这与使用gcc还是g++编译无关
    ————————————————
    版权声明:本文为CSDN博主「wsqyouth」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/u013457167/article/details/80222557
条件编译的几个使用场景

编译预处理包括头文件展开、宏替换、条件编译、去掉注释等。

  1. 检查宏是否被定义,避免重复包含头文件导致的重复定义
    #ifndef THIS_HEADER_FILE
    #define THIS_HEADER_FILE
    /* TODO */
    #endif
    
    #ifdef THIS_HEADER_FILE
    /* TODO */
    #endif
    
  2. 判断编译环境,选择合适的编译器编译代码,如下,当C++编译器编译时,TODO 部分使用C编译器编译,其中,__cplusplus 是C++编译器的保留宏定义
    #ifdef __cplusplus
    extern "C" {
    #endif
    /* TODO */
    #ifdef __cplusplus
    }
    #endif
    
  3. 都是判断一个宏被定义的情况,每个宏都有其存在的意义和作用
2 extern 修饰变量 / 函数
修饰变量
  1. C++ 提供两种变量声明方式:defining declaration 和 referencing declaration,前者简称定义(definition),后者简称声明(declaration),定义会分配内存空间,声明不分配
  2. 若要在多个文件共享全局变量,需要在其中一个文件内包含该变量的定义,其他文件用extern修饰该变量但不初始化,如在a.cpp中定义变量m,在b.cpp中声明变量m
    // a.cpp
    int m;
    
    // b.cpp
    extern int m;
    
  3. 需要注意的是以下均为定义,即使加了extern,所以,如果多个文件用extern声明同一变量的同时初始化,就会出现重复定义的错误
    // 定义变量
    int m;	// 即使没有初始化,也分配了内存空间,默认值为0
    int m = 0;
    extern int m = 0;
    
  4. 还需要注意的是,用extern声明变量的语句也可以在函数体内,并非一定在函数外
  5. 尽量不要在头文件中定义变量,避免重复包含头文件导致的重定义,避免链接导致的重定义
  6. 不要过分依赖使用全局变量;对于常量数据,加const以防误改
修饰函数
  1. 默认函数的链接性是外部的,也就是其他文件可见,extern关键字可加可不加
  2. 常用的方式是将函数设为内部可见,即用static修饰函数,如下,同时给函数的声明和实现添加static,这时其他文件可以定义同名函数
    static int fun();
    static int fun(){}
    
3 其他的一些点
存储持续性和链接性
  1. C++存储持续性类型包括自动存储持续性(如函数内定义的变量)、静态存储持续性(函数外定义,或函数内+static定义的变量)、动态存储持续性(new和delete)、线程存储持续性(C++11,thread_local)
  2. 链接性:外部链接性(不同文件间共享)、内部链接性(文件内函数共享)、无链接性(代码块中共享)
  3. 关于extern修饰变量和函数,主要讨论的即是静态存储持续性和外部链接性
参考
  1. 【C++】函数重载
  2. C++ 是C 的超集吗?可以用C++ 编译器来编译C 代码吗?
  3. C/C++兼容性
  4. 关于extern “C”(详细剖析)
  5. C++ primer plus sixth edition