• C++ :全局变量(extern int a;)、全局函数【可跨文件/模块使用】【只在头文件中做“声明”】


    我们在编译模块中的任意一个文件中书写的变量/函数在此模块中其他文件中都可以被访问到,但是其他编译模块的文件是没有访问此变量的权限的。那么如何跨模块共享变量 / 函数呢?

    答案就是使用 extern. 在这里请在做的各位牢牢记住它的定义:标示所修饰的变量或函数的可能位于其他模块。

    一定要牢牢记住上面的定义, 带着定义我们就可以想明白以下问题:

    1. 为什么在一个 implementation file 中使用一个外部变量要先 extern 声明该变量 (或者导入该变量所在的 header file)?
    2. 为什么 header file 中要使用 extern 声明一个变量?

    一、全局变量

    这样当我们编译某个单元时, 编译器发现了使用 extern 修饰的变量,

    1. 如果正好本模块中有其相关定义, 那么就直接使用;
    2. 如果本模块中没有相关定义, 那么就挂起, 在编译后续其他模块的时候进行查找,
    3. 如果到最后还没有找到, 那么在链接阶段就会报错 ld: symbol(s) not found for architecture x86_64;

    1、正确方式

    1. 在 test1.h 中声明 extern int a;
    2. 在 test1.cpp 中定义 int a = 10; (或者使用 int a; 定义, 这样的话值是默认值 0)
    3. 在 test2.cpp 中 #include "test1.h", 这样便可以在 test2.cpp 中直接使用 a 变量了.

    2、错误方式 

    2.1、错误方式01

    在头文件 test1.h 中直接 extern int a = 10;

    这样属于在头文件中直接定义, 我们已经说了 一个变量可以被多处声明, 但只能定义在一处, 在这种情况下如果有多个 implementation file 都 #include "test1.h", 那么会造成在 obj 文件的 链接 阶段发现多处存在同一个变量的定义, 这时会报错 ld: 1 duplicate symbol for architecture x86_64

    在头文件中定义一个变量属于非常业余的做法, 请不要争相模仿

    2.2、错误方式02

    在头文件 test1.h 中 直接 extern int a = 10; 在 test2.cpp 中直接使用 extern int a;(没有 #include test1.h)

    这样做可以避免多处重复定义的问题, 但是这样的话 test1.h 定义的其他变量与方法都不可以使用了, 必须全部使用 extern *** 的形式进行声明然后使用, 这样会及其得不偿失.

    3、总结

    所以我们可以得出结论:只在头文件中做声明!!!

    真理总是这么简单!

    二、全局函数

    函数与变量类似, 也分为定义与声明. 但是与变量在声明时必须要包含 extern 不同, 由于函数的定义和声明是有区别的:

    • 定义函数要有函数体;
    • 声明函数没有函数体;

    所以函数定义和声明时都可以将 extern 省略掉, 反正其他文件也是知道这个函数是在其他地方定义的, 所以不加 extern 也行.

    所以在 cpp 中, 如果在一个函数前添加了 extern, 那么仅表示此函数可能在别的模块中定义; 或者也可以让我们在只使用了某个头文件的这个方法时不用 #include <***.h>

    1、static使用

    当 static 用于修饰类中的变量/函数,表明该变量/函数是一个静态成员变量/函数

    • 类加载的时候会分配内存
    • 可以通过类名直接访问

    当 static 用于修饰类之外的变量/函数,表明该变量/函数是一个普通的全局静态成员变量/函数

    • 用于修饰变量时表示其存储在全局(静态)区, 不存储在栈上面;
    • 只对本编译模块有效(即使在外部使用 extern 声明也不可以), 不是真正意义的全局(普通的函数默认是 extern 的)
    • 声明与定义时同时发生的
    • 当局部变量不想在函数结束时被释放的时候可以使用 static, 比如函数中要返回一个数组, 不想让这个数组函数结束时被释放, 那么可以使用 static 修饰此局部变量

    static 使变量只在本编译模块内部可见, 这样的话如果两个编译模块各自都有一个 value变量的话, 那么千万不要将两个编译模块内 static 修饰的变量认为是同一份内存, 他们实际上是两份内存, 修改其中一个不会影响另外一个

    2、const

    当 const 单独使用时它就与 static 相同, 而当与 extern 一起合作的时候, 它的特性就跟 extern 的一样了。

    3、ifndef 的使用与意义

    #ifndef 能保证你的头文件在本编译模块只被编译一次(但是多个模块都编译此段代码的话则还是会有重复代码)

     一般格式:

    1. #ifndef <标识>
    2. #define <标识>
    3. ...
    4. ...
    5. #endif

    使得编译器可以根据这个名字是否被定义,再决定要不要继续编译该头文中后续的内容。

    <标识> 在理论上来说可以是自由命名的,但每个头文件的这个<标识>都应该是唯一的。标识的命名规则一般是:

    • 头文件名全大写,前后加下划线,
    • 并把文件名中的“.”也变成下划线,

    这个方法虽然简单,但是写头文件时一定记得写进去。

    三、总结一些 头文件 & 声明 & 定义 的规则

    1. header file 中是对于该模块接口的声明, 接口包括该模块提供给其它模块调用的外部函数及外部全局变量, 对这些变量和函数都需在 header file 中冠以 extern 关键字声明
    2. 模块内的静态函数全局变量需在 implementation file 开头冠以 static 关键字声明
    3. 永远不要在 header file 中定义变量
    4. 如果要用其它模块定义的变量和函数, 直接 #include 其 header file 即可.
    ❝如果工程很大, 头文件很多, 而有几个头文件又是经常要用的, 那么
    1. 把这些头文件全部写到一个 header file 里面去, 比如写到 preh.h
    2. 写一个 preh.cpp, 里面只一句话: #include "preh.h"
    3. 对于 preh.c, 在 project setting 里面设置 create precompiled headers, 对于其他 c++ 文件, 设置 use precompiled header file

    C++ 之头文件声明定义 - 知乎

  • 相关阅读:
    平面上最接近点对(分治法)
    系统内存的探测
    1688接口大全及其解析
    哈希原理及模拟实现并封装unordered系列关联式容器
    2018年五一杯数学建模C题江苏省本科教育质量综合评价解题全过程文档及程序
    远程控制软件
    接入网络技术
    Python常见面试题006 类方法、类实例方法、静态方法有何区别?
    睡眠与健康:你的睡姿可能不对喔~
    通讯录的实现(文件版本)
  • 原文地址:https://blog.csdn.net/u013250861/article/details/127563427