C++存储持续性、作用域和链接性

存储持续性、作用域和连接性

1、存储持续性

​ C++11有四种不同的方案来存储数据,这些方案的区别就在于数据保留在内存中的时间

  • 自动存储持续性:函数定义中声明的变量的存储持续性为自动的。在程序开始执行其所属的函数或代码块时就被创建,执行完函数或代码块后,内存会被释放。

  • 静态存储持续性:在函数外定义的变量和使用关键字static定义的变量的存储持续性都为静态。

    静态变量存储在静态存储区。那么如何声明?

    • 如果变量在代码块中声明,则加上static则声明为静态
    • 如果变量在代码块外声明,则无论有没有static,都是静态变量。如果加上static,则static有另外的含义,表示链接性为内部。

    静态变量默认初始化有两类:静态初始化和动态初始化。静态初始化又包括零初始化和常量表达式初始化。

    • 零初始化:静态变量默认会进行零初始化
    • 常量表达式初始化:在编译器初始化
    • 动态初始化:无法在编译器初始化,则会在编译后初始化。可能原因很多:表达式包含动态变量、需要调用函数返回值等
  • 动态存储持续性:用new运算符分配的内存,直到使用delete运算符将其释放或程序结束为止。期间一直存储在堆内存中。

    并不存在“动态变量”,只是将动态分配的地址赋给一个指针变量。指针本身有他的内存空间,但指针的内存被回收,并不会导致new分配的空间被回收(就是说是指针被回收,不是那块地址被回收)。因此,使用new来申请的内存空间一定要用delete删除。

  • 线程存储持续性(C++11):变量使用关键字thread_local声明,则其生命周期与所述的线程一样长。

2、作用域

作用域(scope)描述了名称在文件(编译单元)的多大范围内可见。例如:函数中定义的变量可在该函数中使用,但不能在其他函数中使用;而在文件中的函数定义之前定义的变量则可在所有函数中使用。
​ 编译器可以确定四种不同类型的作用域:

  1. 文件作用域——任何在代码块之外声明的标识符都具有文件作用域,表示这些标识符从声明到源码结尾处都是可以访问的
  2. 函数作用域——goto语句
  3. 代码作用域——花括号之间的全部语句
  4. 原型作用域——是C++程序中最小的作用域。在函数原型声明时形式参数的作用范围就是函数原型作用域。如:int p(int c);
3、标识符连接属性

​ 首先要理解程序的实现:翻译、执行

  • 翻译:将源代码转换成可执行的机器指令,即一个源程序到一个可执行程序的过程
  • 执行:执行可执行程序
    在这里插入图片描述

连接属性

​ 当组成一个程序的各个源文件分别被编译之后,所有的目标文件以及那些从一个或多个库函数中引用的函数链接在一起,形成可执行程序。
标识符的链接属性决定如何处理在不同文件中出现的标识符。标识符作用域与它的链接属性有关,但这两个属性并不相同。

​ 官方一点的说法就是连接性描述了名称如何在不同单元间共享

​ 连接属性有三种:

  • 外部链接:连接性为外部的名称可在文件间共享。不管声明多少次,存在于多少个源文件,都是属于同一个个体。
  • 内部链接:连接性为内部的名称只能由一个文件中的函数共享。也就是说,在同一个源文件所有声明都是同一个个体,但在其他源文件中多个声明分属不同个体。
  • 无连接:名称不能共享。总是当成单独个体,也就是多次声明被当做多个不同的实体。
4、关键字static和关键字extern

​ 作用:用于在声明中修改标识符连接属性的关键字

typedef char* a;
int b;
int p(int d)
{
	int e;
	int f(int g);
}
4.1、static的用法
  1. 对于缺省情况下为extern链接的属性声明,在声明前加static,会使这个标识符的链接属性变为internal,变成这个源文件私有,防止被其它源文件调用

    上述代码所示,b的链接属性为extern

    static int b;
    

    加上static关键字后,b的链接属性改变internal,变为源文件私有。

  2. 对于缺省情况下为none链接的属性声明,在声明前加static,并不会改变这个标识符的链接属性,也不会改变它的作用域,但是会把它的自动存储持续性改为静态存储持续性

4.2、extern的用法
  1. 它与"C"一起连用时,如: extern “C” void fun(int a, int b); 则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的

  2. 引用同一个文件中的变量

    #include <iostream>
    using namespace std;
    
    void fun1();
    
    int main(){
    	cout << nums;
    }
    
    int nums = 123456;
    
    

    ​ 按照此执行顺序,变量nums在main函数后边进行声明和初始化的话,在main函数中不能直接引用nums这个变量。因为编译到这句话时,找不到nums这个变量的声明。

    ​ 如果不想改变nums的声明位置,但想在main中直接使用该变量,此时可以使用extern这个变量。首先利用extern声明一下这个变量是存在的,然后让编译器到其他地方找定义。代码如下:

    #include <iostream>
    using namespace std;
    
    void fun1();
    
    int main(){
    	extern int nums;
    	cout << nums;
    }
    
    int nums = 123456;
    
  3. 在头文件中: extern int gg; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块或者其他模块中使用,**记住它是一个声明不是定义!**举个例子,假设B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可。在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值