当前位置: 首页 > article >正文

C++学习笔记3——存储持续性、作用域和链接性

1. 存储持续性

  • 自动存储持续性:在函数中定义或声明的变量存储持续性为自动的,它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,使用的内存被释放;
  • 静态存储持续性:在函数定义外定义的变量和使用关键字static定义的变量存储持续性为静态,在程序整个运行过程中都存在
  • 动态存储持续性:用new运算符分配的内存,直到delete运算符释放或程序结束为止一直存在。

2. 作用域和链接

作用域描述文件在多大范围内可见。

  • 作用域为局部的变量只在定义它的代码块(花括号括起来的一系列语句,如函数体)中可用。
  • 作用域为全局(文件作用域)的变量在定义位置到文件结尾之间可用
  • 在类中声明的变量作用域为整个类,在名称空间中声明的变量作用域为整个名称空间。

C++函数的作用域可以是整个类或整个名称空间,但不能是局部的(因为不能在代码块中定义函数)

3. 不同存储方式

3.1 自动存储持续性变量

默认情况下,函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性,即只能在函数(代码块)内访问。
自动变量的数量随着函数的运行或结束而有所增减,因此函数需要栈对自动变量进行管理。

3.2 静态存储持续性变量

C++为静态存储持续性变量提供了三种链接性:

  • 外部链接性(可在其他文件中访问),创建需要在代码块外面声明
  • 内部链接性(只能在当前文件中访问),需要在代码块外面声明并使用static
  • 无链接性(只能在当前函数或代码块中访问),需要在代码块内声明并使用static

静态变量的数目在程序运行期间保持不变,编译器分配固定内存块存储所有静态变量,初始化为0,在程序执行期间一直存在。

3.2.1 外部链接性

变量只能有一次定义,使用引用声明extern引用已有的变量,不分配存储空间。如果要在多个文件中使用外部变量,只需在一个文件中包含该变量的定义,但在使用该变量的其他所有文件中使用extern关键字进行声明。

同名的局部变量会隐藏全局变量。
在变量前使用“::”运算符可用使用该变量的全局版本。

3.2.2 内部链接性

在代码块外部使用static限定符定义变量,该变量的链接性为内部,只能在当前文件中使用。

文件中的静态变量与另一个文件中的常规外部变量同名,静态变量会隐藏常规外部变量。如下代码展示了内部链接性与外部链接性。代码位于两个文件中,通过编译链接后运行。
main.cpp:

// main.cpp
#include <iostream>
int tom = 3;
int dick = 30;
static int harry = 300;

void remote_access();

int main()
{
    using namespace std;
    cout << "main() reports the following addresses:" << endl;
    cout << &tom << " = &tom, "<<&dick <<" = &dick, ";
    cout << &harry << " = &harry"<<endl;
    remote_access();
    return 0;
}

twofile.cpp:

#include <iostream>
extern int tom;
static int dick = 10;
int harry = 200;

void remote_access()
{
    using namespace std;
    cout <<"remote_access() reports the following addresses:"<<endl;
    cout << &tom << " = &tom, "<<&dick <<" = &dick, ";
    cout << &harry << " = &harry"<<endl;
}

使用g++在linux下进行编译,生成可执行文件:

g++ main.cpp twofile.cpp -o main

运行结果如下:
在这里插入图片描述可见两个函数中使用了同样的tom变量,但使用了不同的dick变量和harry变量。

3.3.3 无链接性

在代码块中用static限定符定义变量,则变量没有链接性,只能在代码块中使用,但在该代码块不运行时依然存在,因此在两次调用函数之间变量的值保持不变。

如果初始化了静态局部变量,则只在启动时进行一次初始化,再次调用时不再会像自动变量一样进行初始化。

以下代码中展示了自动变量与静态局部变量。

#include <iostream>
const int ArSize = 10;

void strcount(const char* str);


int main()
{
    using namespace std;

    char input[ArSize];
    char next;

    cout <<"Enter a line"<<endl;
    cin.get(input, ArSize);

    while(cin)
    {
        cin.get(next);
        while(next != '\n')
            cin.get(next);
        strcount(input);
        cout << "Enter next line (empty line to quit)"<<endl;
        cin.get(input, ArSize);
    }

    cout<<"Bye"<<endl;
    return 0;
}

woid strcount(const char * str)
{
    using namespace std;
    static int total = 0;
    int count = 0;

    cout <<"\""<<str<<"\"contains";
    while(*str++)
        count ++;
    total += count;
    cout << count << " characters"<<endl;
    cout << total << " characters total"<<endl;
}

运行结果如下:
在这里插入图片描述可见count变量每次都变化,而total变量在原有的基础上累加。

3.4 限定符

  • volatile限定符

表示即使程序代码没有对内存单元进行修改,其值也可能发生变化。

编译器可能进行某种优化:假设编译器发现程序在几条语句中两次使用了某个变量的值,则编译器可能不是让程序查找这个变量两次,而是将这个值缓存到寄存器中。这种优化假设两次使用之间变量的值不会变化。如果不将变量声明为volatile,则编译器将进行这种优化,声明为volatile相当于禁用这种优化。

  • mutable限定符

可用用来指出,即使结构(或类)变量为const,其某个成员也可能被修改。
如下的例子:

struct data 
{
	char name[30];
	mutable int accesses;
	……
};
const data veep = {"Claybourne Clodde", 0, ……}strcpy(veep.name, "Joye Joux");// 不允许
veep.accesses++;//允许

veep结构被声明为const,禁止修改其成员;但声明为mutable的accesses成员可以被修改。

  • const限定符

const限定符定义的全局变量具有内部链接性,因此可以包含在头文件中。被多个文件包含也不会产生冲突。

3.5 使用new分配的内存

使用new分配的动态内存不受作用域与链接性规则控制,其值取决于使用new和delete的位置。虽然存储方案概念不适用于动态内存,但适用于用来跟踪动态内存的自动和静态变量指针变量。
如下面的例子:

float * p_fees = new float[20];

由new分配的80个字节(假设float为4个字节)的内存将一直保留在内存中,直到使用delete进行释放。但当包含该声明的代码块运行完毕后,p_fees指针将消失。如果还需要使用这80个字节的内容,则需要将其地址传递或者返回。如果将p_fees声明为外部链接,则在代码块外部也可以使用该指针。


http://www.kler.cn/a/374221.html

相关文章:

  • 【C++】线程启动、结束与创建线程写法
  • WebSocket 实现指南
  • R语言基础| 中级绘图
  • vulnhub靶场-potato(至获取shell)
  • python 如何调整word 文档页眉页脚
  • c# Record关键字
  • Web应用程序安全与风险
  • C++——String类讲解
  • Linux下的pipe函数详解
  • 干货--并发编程提高-计算CPU利用率(二十二)
  • smartconnect base_addr offset_addr
  • WPF中如何解决DataGrid的Header没有多余的一行
  • echart实现地图数据可视化
  • 计算机系统中的文件和文件夹
  • Nop平台核心代码阅读导引
  • gem5运行简单RISC-V全系统模拟
  • Docker 实践与应用举例教程:从入门到精通
  • LinkedList 分析
  • 【STM32】OLED显示屏
  • 汽车软件融合分析
  • spring boot 启动配置name: @project.artifactId@报错
  • 【protobuf(1)】首次理解与实践
  • Linux中NFS配置
  • np.expand_dims函数
  • 校园社团信息管理:Spring Boot技术的应用与优化
  • RHCE 配置文件