Javer 学 c++(十一):内存模型篇

71 阅读4分钟

总览

主要讲了 c++ 中程序的内存模型的四个区域:代码区、全局区、栈区、堆区以及 new 关键字的使用

内存分区模型

c++ 程序在执行时,内存大致可以分为以下四个区域:

  • 代码区:存放函数体的二进制代码,由操作系统进行管理
  • 全局区:存放全局变量、静态变量以及常量
  • 栈区:由编译器自动分配释放,存放函数的参数值、局部变量等
  • 堆区:由程序员分配和释放,若程序员不释放,则程序结束时由操作系统回收

分区意义:分区方便管理,每一部分的生命周期不同,而且每一部分的申请释放权限可能也不同

代码区

程序编译后,生成可执行程序(不同操作系统的后缀不同),在程序未执行前只存在两个区域:代码区以及全局区

功能:存放 cpu 执行的机器指令

代码区是共享的,共享的目的是对于被频繁执行的程序,只需要在内存中有一份代码即可

代码区是只读的,防止程序意外地修改了它的指令

全局区

功能:存放全局变量和静态变量,同时还包含了常量区,字符串常量和其他常量也存放在此

此区域的数据在程序结束后由操作系统进行释放

#include <iostream>
using namespace std;
#define MAX 1000

// 2. 全局变量
int global_a  = 10;
int global_b  = 20;

// 5. 常量 - const修饰的全局变量
constexpr int const_global_a = 10;
constexpr int const_global_b = 20;

int main() {

    // 全局区:存放全局变量、静态变量以及常量

    //1. 普通局部变量
    int a = 10;
    int b = 20;
    // 0x16d9c6fe8     0x16d9c6fe4
    cout << &a << "     " << &b << endl;

    // 2. 0x102440000     0x102440004
    cout << &global_a << "     " << &global_b << endl;

    // 3. 静态变量
    static int s_a = 10;
    static int s_b = 20;
    // 0x102440008     0x10244000c
    cout << &s_a << "     " << &s_b << endl;

    //4. 常量 - 字符串常量
    // 0x100905b4a     0x100905b50
    cout << &"hello" << "     " << &"world" << endl;

    // 5. 0x100cc1c18     0x100cc1c1c
    cout << &const_global_a << "     " << &const_global_b << endl;

    // 6. 常量 - const修饰的局部变量
    const int const_a = 10;
    const int const_b = 20;
    // 0x16f13efe0     0x16f13efdc
    cout << &const_a << "     " << &const_b << endl;


    return 0;

}

image.png

由上面代码及图可得,全局区包含情况如下:

  • 普通局部变量❌
  • 全局变量 ✅
  • 静态变量 ✅
  • 常量-字符串常量 ✅
  • 全局常量(const修饰全局变量)✅
  • 局部常量(const修饰局部变量)❌

栈区

功能:存放函数的参数值、局部变量等,由编译器自动分配释放

#include <iostream>
using namespace std;
#define MAX 1000

//栈区的数据由编译器管理开辟和释放
int* solve() {
    int a = 10;
    return &a;
}

int main() {

    int * p = solve();
    // 10,这次可以打印正确的,是因为编译器做了一次保留
    cout << *p << endl;
    // 1,第二次这个数据就被释放了
    cout << *p << endl;
    return 0;

}

由代码总结,不要返回函数内局部变量的地址,因为返回后编译器就会做回收,指针就失效了

堆区

  • 由程序员分配释放;若程序员没有释放,程序结束时由操作系统进行释放
  • 在 c++ 中主要利用 new 在堆中开辟空间
#include <iostream>
using namespace std;
#define MAX 1000

int* solve() {
    // 利用 new 关键字,可以将数据开辟到堆区
    int * p = new int(10);
    return p;
}

int main() {

    int * p = solve();
    // 10
    cout << *p << endl;
    // 10
    cout << *p << endl;

    free(p); // 记得释放内存,不然会提示leak
    return 0;

}

new 操作符

  • c++ 利用 new 来在堆上开辟空间
  • 语法: new 数据类型(值);
  • 堆区的数据由程序员手动开辟,释放时使用 delete 操作符
  • 利用 new 创建的数据,会返回该数据对应类型的指针
#include <iostream>
using namespace std;
#define MAX 1000

int * getNum() {
    // new关键字用法,返回的是这个数据类型的指针
    int * p = new int(10);
    return p;
}

void test() {
    int * p = getNum();
    // 10
    cout << *p << endl;
    delete p;
    // -491814896  内存空间已被释放
    cout << *p << endl;
}

// 利用 new 开辟数组
void getArr() {
    int * arr = new int[10];
    // 赋值
    for (int i = 0; i < 10; i++) {
        arr[i] = i;
    }
    // 打印
    for (int i = 0; i < 10; i++) {
        cout << arr[i] << endl;
    }
    // 释放数组的时候,需要使用 delete[]
    delete[] arr;
}

int main() {

    test();

    getArr();

    return 0;

}