C++内存模型

内存模型

  • 自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。C++有两种存储持续性为自动的变量。
  • 静态存储持续性:在函数定义外定义的变量使用关键字static定义的变量的存储持续性都为静态。它们在程序整个运行过程中都存在。C++有3种存储持续性为静态的变量。
  • 线程存储持续性(C++11):当前,多核处理器很常见,这些CPU可同时处理多个执行任务。这让程序能够将计算放在可并行处理的不同线程中。如果变量是使用关键字thread_local声明的,则其生命周期与所属的线程一样长。
  • 动态存储持续性:用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序结束为止。这种内存的存储持续性为动态,有时被称为自由存储(free store)或堆(heap)

c++ 变量的默认类型为自动存储持续性的 C语言中auto关键字可以指出当前的变量为局部自动变量,但是c++中auto有了别的作用,即自动类型推导,使用auto来指定变量为自动存储的手段将不再合法

C语言中还支持寄存器变量使用register关键字来让寄存器存储自动变量,在C++11之后这种用法失效了,而是变成了用于指定一个变量为自动变量,这与C语言以前的auto完全相同

静态存储持续性与static关键字

写在函数体外的变量就是静态持续性

int x =1000;  //静态持续性,具有外部链接
static int y =50; // 静态持续性,内部链接
int main(){}
void func(){
static int c=0; // 静态持续性 ,链接
}

在C++中,链接属性(linkage)决定了标识符(变量、函数、类等)在不同翻译单元(即源文件)中的可见性。

  1. 外部链接: 可以在程序的任何翻译单元中访问,在整个程序中必须是唯一的定义
  2. 内部链接:仅在该翻译单元可见,不同翻译单元可以有同名的内部链接实体
  3. 无连接:仅在定义它的作用域内可见

C++中const全局变量默认内部链接

C++有“单定义规则”(One Definition Rule,ODR),该规则指出,变量只能有一次定义。
多文件程序是不允许有相同名字的具有外部链接的变量的

// file1
int x =10;
...
--------------------------
//file 2
int x =20;
int main(){}
g++ file1 file2

会报错,是不能有相同名称外部变量的
此时你需要使用static关键字将一个变量设置为内部链接,或者使用如下的extern变量让一个定义变成引用声明而不是定义声明

所以C++除了传统的变量定义声明,通过extern关键字实现了引用声明,用来指示这个变量来自其他的源文件,且不能进行初始化,如果初始化了就代表他是定义声明 , 引用声明不会分配存储空间

extern int x; // 引用声明
extern int y=1; // 定义声明

函数默认是静态持续性的,也可以使用static让其具备内部链接性(请注意函数的定义也要满足单定义规则),使用外部的函数可以使用extern关键字声明

static无法修饰模板,类、结构 ,你可以使用匿名名称空间来让这些内容具有内部链接性

说明符和限定符

存储说明符

  1. auto C++11已经不再是说明符
  2. register
  3. static
  4. extern
  5. thread_localC++11新增的线程使用
  6. mutable

限定符

  1. volatile 表面即使程序没有对单元进行修改他的值也可能发生变化,例如硬件改变,用于嵌入式编程,避免编译器对这个值进行优化

  2. mutable 可以用它指出即使结构或者是类变量为const,其中的某个成员也可以被修改

    struct data{
    char name[30];
    mutable int accesses;
    };
    const data v{"bueryi",0};
    v.accesses++; // 虽然结构为const,但是你仍然可以修改被mutable修改的变量
    
  3. const C++ 与C语言不同,const限定符修饰的全局变量链接性为内部的(C语言则是外部的),也就是说C++中全局const定义就像是使用static一样

    const int x = 10; // 和 static const int x =10; 相同
    

    这样在你将常量放入头文件的时候,可以更加轻松地导入文件,如果他和其他变量同样是外部的话,在你不同源文件都包含相同的头文件时,会由于有多个具有外部链接的变量而出错,违反单变量规则
    也就是这样只有一个文件可以包含前面的声明,其他文件必须使用extern关键字

    extern const int x; 
    

    所以C++中const全局变量被设置为内部链接的形式,每一组文件都会有自己的一组常量,而不是所有的文件共享一组常量,每个定义都是所属文件私有的

    如何声明外部链接的常量呢?使用extern关键字

    extern const int x =50;
    

    这个行为与普通变量不同,普通变量默认就是外部链接,无需使用extern指定

    下面我举例说明一下测试

    //head.h
    #pragma once                                                                             
    const int x=10; //这是内部链接的常量
    //file0.cpp
    extern const int y=123; //直接声明了一个外部链接的常量
    //file1.cpp
    #include <iostream>
    #include "head.h"
    void f1()
    {
        using std::cout;
        extern const int y;
        cout << "file1: \n";
        cout << "x= "<< x << " &x="<<  &x<<std::endl;
        cout << "y= "<< y << " &y="<<  &y<<std::endl;                                        
    }
    //file2.cpp
    #include <iostream>
    #include "head.h"
    void f2()
    {
        using std::cout;
        extern const int y;
        cout << "file2: \n";                                                                 
        cout << "x= "<< x << " &x="<<  &x<<std::endl;
        cout << "y= "<< y << " &y="<<  &y<<std::endl;
    }
    //main.cpp
    #include <iostream>
    extern void f1(); //调用外部的函数
    extern void f2();
    int main()
    {
        f1();
        f2();                                                                                
        return 0;
    }
    

    所有的文件如下所示(请忽略我的cpplearn
    在这里插入图片描述
    编译运行(我没有指定输出名字,默认是a.out, 我还使用了通配符,你可以在这里找到通配符的使用)
    在这里插入图片描述
    你可以看到file1file2他们的x地址不同,这体现了前面所说的,const默认是内部链接,不同的文件导入会产生各自的常量,他们的地址不同,而y是外部const变量,你可以看到不同文件引用y他们的地址是相同的(这与常规引用外部变量的行为一直,extern是声明定义不会初始化储存空间)

动态变量

C++使用new关键字来从堆中获取内存空间,他的生命周期持续到你调用delete为止
支持调用时初始化

int* arr2 = new int[5]{1, 2, 3, 4, 5};

定位new

char buffer[300];  // 预分配内存
double* pt = new (buffer) double[10];  // 在指定内存构造对象

注意此时不能使用delete []来释放空间,因为他不在堆中,而是你自己预分配的地方(除非预分配的内存也是用new分配的)

new/deletemalloc/free的区别

  1. new会调用构造函数和析构函数,malloc不会
  2. new会返回正确的指针,malloc则是void*指针你需要强制类型转换
  3. new失败了会抛出bad_alloc异常,而malloc则会返回NULL
  4. new会自动计算分配内存大小,malloc则是要手动计算
  5. new支持重载,malloc不行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值