C++学习 内存分区模型

内存分区模型

C++程序在执行时,将内存大方向划分为4个区域

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

意义:

        不同区域存放的数据有着不同的生命周期,使编程更加灵活。

 

程序运行前

程序运行前划分的两个区域:

 

在程序编译后,生成可执行exe可执行程序,但是未执行程序前分为两个区域

  • 代码区:

        存放cpu执行的机器指令。

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

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

  • 全局区

        全局变量和静态变量存放在此。

        全局区还包含了常量区,字符串常量和其他常量存放在此。

        该区域数据在程序结束后由操作系统释放。

 

#include 
using namespace std;

// 定义全局变量
int g_a = 10;
int g_b = 10;

// const修饰的全局变量(全局常量)
const int c_g_a = 10;
const int c_g_b = 10;

int main()
{
    // 全局区
    // 全局变量、静态变量、常量

    // 创建普通的局部变量
    int a = 10;
    int b = 10;
    cout << "   局部变量a的地址    " << (long long)&a << "  局部变量b的地址  " << (long long)&b << endl;        // 局部变量a的地址6553116局部变量b的地址6553112
    cout << "   全局变量g_a的地址  " << (long long)&g_a << " 全局变量g_b的地址    " << (long long)&g_b << endl; // 全局变量g_a的地址4206608全局变量g_b的地址4206612 可见局部变量和全局变量不在一个区

    // 静态变量 在普通变量前加static关键字修饰
    static int s_a = 10;
    static int s_b = 10;
    cout << "   静态变量s_a的地址   " << (long long)&s_a << " 静态变量s_b的地址    " << (long long)&s_b << endl; // 静态变量s_a的地址4206616静态变量s_b的地址4206620

    // 常量
    // 字符串常量
    cout << "   字符串常量的地址    " << (long long)&"hello" << endl; // 字符串常量的地址    4210904

    // const修饰的变量 : const修饰的全局变量,const修饰的局部变量
    cout << "   const修饰的全局变量(全局常量)    " << (long long)&c_g_a << "   const修饰的全局变量(全局常量)    " << (long long)&c_g_b << endl; //  const修饰的全局变量    4210688   const修饰的全局变量    4210692

    const int c_a = 10;
    const int c_b = 10;
    // const修饰的局部变量
    cout << "   const修饰的局部变量    " << (long long)&c_a << "   const修饰的局部变量    " << (long long)&c_b << endl; //  const修饰的局部变量    6553108   const修饰的局部变量    6553104
}

 

C++学习 内存分区模型_第1张图片

程序运行后

栈区:

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

注意:

        不要返回局部变量的地址,栈区开辟的数据由编译器自动释放。

堆区:

        由程序员分配释放,若程序员不释放,程序结束时由操作系统回收。

        在C++中主要利用new在堆区开辟内存

 栈区注意示例:不要返回局部变量地址

#include 
using namespace std;

// 栈区数据的注意事项-> 不要返回局部变量的地址
// 栈区的数据由编译器管理开辟和释放

int* func()
{
    // 局部变量
    int a = 10; // 存放在栈区 栈区数据会在函数执行完自动释放
    return &a; // 不要这么返回
}
int main()
{
    int * p = func();
    cout << *p << endl; // 10 不同编译器效果不同 我的vscode会直接抛异常 Dev-C++中输出为前面的数值 二次输出为0
    cout << *p << endl; // 0
}
#include 
using namespace std;

// 堆区开辟数据
int *func()
{
    // 利用new关键字 将数据开辟到堆区
    // 指针本质也是局部变量 在栈空间,但是指针保存的数据在堆区
    int *p = new int(10);
    return p;
}

int main()
{
    int *p = func();
    cout << *p << endl; // 10
    cout << *p << endl; // 10
    return 0;
}

四区说完后,再说一下new这个关键字

C++中利用new操作符在堆区开辟数据

堆区开辟的数据,由程序员手动开辟与释放,释放使用操作符delete

语法:

        new 数据类型

利用new创建的数据,会返回该数据对应的类型的指针。

 

#include 
using namespace std;

// new 关键字

// 基本语法 new 数据类型
// int *func()
// {
//     // 在堆区利用new创建整型数据
//     int *p = new int(10);
//     return p;
// }

// 堆区开辟数组
void test()
{
    int *arr = new int[4];
    for (int i = 0; i < 4; i++)
    {
        arr[i] = i + 1; // 数组赋值 1-5
    }
    // 打印
    for (int i = 0; i < 4; i++)
    {
        cout << arr[i] << "\t";
    } // 1       2       3       4
    // 数组释放
    delete[] arr;
    for (int i = 0; i < 4; i++)
    {
        cout << arr[i] << "\t";
    } //    14893472        0       14886672        0
}

int main()
{
    // int *p = func();
    // cout << *p << endl; // 10
    // cout << *p << endl; // 10
    // // 手动释放内存
    // delete p;
    // cout << *p << endl; // 15090080 已经释放了
    test();
    return 0;
}

如果是二维数组的话进行内存释放如下:

for (int i = 0; i < 4; ++i) {
    delete[] arr[i];  // 释放每一行的内存
}
delete[] arr;  // 释放指向行指针的内存

你可能感兴趣的:(C++,学习)