【C++上层应用】1. 内存管理

【 0. 内存管理 】

  • C++ 程序中的内存分为以下个部分:

    • 栈区(stack) :函数内部 声明的所有变量 默认都占用栈内存 ,由 编译器自动分配与释放 。存放为运行时函数分配的局部变量、函数参数、返回数据、返回地址等。其操作类似于数据结构中的栈。如声明 int a;系统自动在栈空间中为a开辟空间。
    • 堆区(heap) :程序中 未使用的内存 ,在程序运行时可用于 用户动态分配内存 。如C中的malloc以及C++中的new,如果程序员没有释放,程序结束时可能有OS回收。
    • 全局区(静态区static)存放全局变量、静态数据、常量。程序结束后由系统释放。全局区分为已初始化全局区(data)和未初始化全局区(bss)。
    • 常量区(文字常量区):存放常量字符串,程序结束后由系统释放。
    • 代码区:存放函数体(类成员函数和全局区)的二进制代码。
  • 实例
    p1和p2本身是在栈中的,但他们指向的地址是堆空间。

char*p1=(char*)malloc(10);
int*p2=new int(10);

【 1. new、delete 】

  • 在 C++ 中,我们可以使用 new 运算符给定类型(可以是包括数组在内的任意内置的数据类型,也可以是包括类或结构在内的用户自定义的任何数据类型)的变量在运行时分配堆内的内存,这会返回所分配的空间地址
  • 如果我们不再需要动态分配的内存空间,可以使用 delete 运算符 删除之前由 new 运算符分配的内存

1.1 new 分配内存

  • 基本语法
new data-type;

1.1.1 简单类型

  • 基本语法
    定义一个指向 指定类型(这里指向的是double)的指针,然后请求内存,该内存在执行时被分配:
double* pvalue  = NULL; // 初始化为 null 的指针
pvalue  = new double;   // 为变量请求内存

/*或者*/

double* pvalue = new double;   // 创建1个指针,可以指向double类型的变量。
  • 实例
#include<iostream>
using namespace std;
int main()
{
    double n=3.14;
    double* pvalue= new double;  //创建1个可以指向double类型的指针 
	pvalue=&n;     // 将指针指向n
  
    cout<<*pvalue;
    return 0;
}

在这里插入图片描述

1.1.2 检查内存是否分配成功

  • 如果自由存储区已被用完,可能无法成功分配内存,所以建议检查 new 运算符是否返回 NULL 指针,并采取以下适当的操作:
double* pvalue  = NULL;
if( !(pvalue  = new double ))
{
   cout << "Error: out of memory." <<endl;
   exit(1);
}

1.2 delete 释放内存

  • 在任何时候,当我们觉得某个已经 动态分配内存的变量不再需要使用时,我们可以使用 delete 操作符释放它所占用的内存,基本语法:
delete pvalue;        // 释放 pvalue 所指向的内存
  • 实例
#include <iostream>
using namespace std;

int main ()
{
   double* pvalue  = NULL; // 创建1个指向 null 的double类型指针
   pvalue  = new double;   // 为变量请求内存

   *pvalue = 29494.99;     // 在分配的地址存储值
   cout << "Value of pvalue : " << *pvalue << endl;

   delete pvalue;         // 释放内存

   return 0;
}

在这里插入图片描述

【 2. 数组的动态内存分配 】

2.1 一维数组

  • 基本语法:
    通过动态内存分配,申请一个长度为 n 的数组。
int *array=new int [n]//申请堆内存,将指针pvalue指向该内存

/* 上面的一行代码可替换为如下:
int* array = NULL;  // 初始化为 null 的指针
array = new int[n]; // 申请堆内存,将指针array 指向该内存
*/

//释放内存
delete [] array;
  • 实例
    通过动态内存分配 生成一个元素数量为4的一维数组,令 a[i]=i,最后输出数组。
#include<iostream>
using namespace std;
int main()
{
    int n=4;
    int *a=new int[n]; //创建1个动态数组a
    
	for(int i=0;i<n;++i)
    	a[i]=i;

    for(int i=0;i<n;++i)
       cout<<a[i]<<" ";
    return 0;
}

在这里插入图片描述

2.2 二维数组

  • 基本语法:
    通过动态内存分配,创建一个二维的 mxn 数组。
// 动态分配内存
int **array;
array = new int *[m];
// 上面的两行代码可简化为:int **a=new int *[m];
for( int i=0; i<m; i++ )
{
    array[i] = new int [n]  ;
}


//释放内存
for( int i=0; i<m; i++ )
{
    delete [] arrary[i];
}
delete [] array;
  • 实例
    生成一个3行4列的动态数组,令 a[i][j]=i+j,最后输出数组。
#include<iostream>
using namespace std;
int main()
{
    int m=3,n=4;

	// 动态分配内存
    int **a=new int *[m];
	for(int i = 0;i<n;i++)
   		a[i] = new int[n];

    // 为数组赋值 
	for(int i=0;i<n;++i)
	{
        for(int j=0;j<n;++j)
            a[i][j]=i+j;
    }

    // 输出数组 
    for(int i=0;i<n;++i)
	{
        for(int j=0;j<n;++j)
        {
            cout<<a[i][j];
            if (j==n-1)
                cout<<endl;
            else
                cout<<" ";
        }
    }

    //释放申请的堆内存   
    for(i=0; i<m; i++){
        delete [] a[i];   
    }
    delete [] a;
   
    return 0;
}

在这里插入图片描述

2.3 三维数组

  • 基本语法:
    通过动态内存分配,创建一个三维的 mxnxh 数组。
// 动态分配内存
int ***array;
array = new int **[m];
for( int i=0; i<m; i++ )
{
    array[i] = new int *[n];
    for( int j=0; j<n; j++ )
    {
        array[i][j] = new int [h];
    }
}

//释放内存
for( int i=0; i<m; i++ )
{
    for( int j=0; j<n; j++ )
    {
        delete[] array[i][j];
    }
    delete[] array[i];
}
delete[] array;
  • 实例:
#include <iostream>
using namespace std;

int main()
{   
    int i,j,k;   // p[2][3][4]

    int ***p;
    p = new int **[2]; 
    for(i=0; i<2; i++) 
    { 
        p[i]=new int *[3]; 
        for(j=0; j<3; j++) 
            p[i][j]=new int[4]; 
    }

    //输出 p[i][j][k] 三维数据
    for(i=0; i<2; i++)   
    {
        for(j=0; j<3; j++)   
        { 
            for(k=0;k<4;k++)
            { 
                p[i][j][k]=i+j+k;
                cout<<p[i][j][k]<<" ";
            }
            cout<<endl;
        }
        cout<<endl;
    }

    // 释放内存
    for(i=0; i<m; i++) 
    {
        for(j=0; j<n; j++)
        {
            delete [] p[i][j];
        }
        delete [] p[i];
    }
    delete [] p;
    
    return 0;
}

在这里插入图片描述

【 3. 对象的动态内存分配 】

  • 对象的动态内存分配与简单的数据类型类似。
  • 实例:
    为一个包含四个 Box 对象的数组分配内存,构造函数将被调用 4 次,同样地,当删除这些对象时,析构函数也将被调用相同的次数(4次)。
#include <iostream>
using namespace std;

class Box
{
   public:
      Box() 
      { 
         cout << "调用构造函数!" <<endl; 
      }
      ~Box() 
      { 
         cout << "调用析构函数!" <<endl; 
      }
};

int main( )
{
   Box* myBoxArray = new Box[4];

   delete [] myBoxArray; // 删除数组
   return 0;
}

在这里插入图片描述

【 4. 其他内存管理函数 】

4.1 malloc() 、free()

  • 头文件:
    C:#include <stdlib.h>
    C++ :#include <cstdlib>
  • malloc() 函数 从堆(heap)中分配一块指定大小的内存区域,并返回一个指向这块内存的指针。malloc 分配的内存不会被自动初始化,它包含未定义的值。基本语法:
    • size:需要分配的内存大小(以字节为单位)。
    • 成功时,返回指向分配内存的指针, void* 类型,使用时通常需要进行 类型转换
      失败时,返回 NULL 或 nullptr(在 C++11 及以后版本中)。
void* malloc(size_t size);
  • free() 函数 释放 指针 指向的存储空间,被释放的空间通常被送入可用存储区池。使用 malloc 分配的内存必须使用 free 函数 来释放,不能使用 C++ 的 delete 运算符 。释放内存后,将指针设置为 NULL 是一个好习惯,可以防止野指针问题。基本语法:
    • ptr:指向要释放的内存的指针。
    • 无返回值。
void free(void* ptr);
  • 例程
int *array = (int *)malloc(10 * sizeof(int));// 申请1个足够存储10个整数的内存

free(array); //释放该内存
  • malloc/free 和 new/delete 对比
    在 C 语言中的malloc() 函数,在 C++ 中仍然存在,但建议尽量 不要使用 malloc() 函数
malloc/freenew/delete
malloc/free 是C/C++的标准库 函数new/delete是C++的 运算符
malloc申请的堆区 内存不会初始化new是 内存自动初始化
malloc需要 用户计算好字节数来申请空间new 自动计算分配空间
malloc函数 返回值是void*,在使用时需要进行类型强制转换在 new 后面直接写明类型
malloc 申请内存失败会返回一个NULL ,在使用的时候必须进行判空new 申请内存失败后会抛出异常
对于自定义数据类型,malloc/free只开辟空间(没有初始化),不会调用构造/析构函数对于自定义数据类型,new不只是分配了内存,它还 创建了对象 。申请、销毁时会触发构造/析构函数。
malloc/free 都是程序员手动从 堆区 申请,然后手动释放new/delete都是程序员手动从 堆区 申请,然后手动释放
  • 实例
#include <iostream>
#include <cstdlib> // 包含malloc函数的头文件
using namespace std;

int main() {
    // 分配足够存储10个整数的内存
    int *array = (int *)malloc(10 * sizeof(int));

    // 检查分配是否成功
    if (array == NULL ) {
        // 如果分配失败,处理错误
        return -1;
    }

    // 使用分配的内存
    for (int i = 0; i < 10; ++i) {
        array[i] = i*i; // 初始化数组元素
    }

    // 打印数组元素
    for (int i = 0; i < 10; ++i) {
        cout << "array[" << i << "]= " << array[i] << endl;
    }

    // 释放分配的内存
    free(array); // 注意:只能使用free来释放malloc分配的内存

    return 0;
}

在这里插入图片描述

4.2 memset 内存初始化

  • 头文件
    C:#include <string.h>
    C++:#include <string>
  • memset()函数 是 C 语言标准库中的一个函数,用于 将一块内存区域的前 n 个字节中的每个字节设置为指定的值 。基本语法:
    • ptr:指向要填充的内存块的指针。
    • value:要设置的值,通常是一个介于 0 到 255之间的整数 ,因为 memset 一次只设置一个字节大小。
    • num:要 设置的字节数
    • 返回指向大小为 num 内存块的原始指针 ptr,如果错误发生返回NULL。
void* memset(void* ptr, int value, size_t num);
  • 实例
#include <stdio.h>
#include <string.h> // 包含 memset 函数的头文件

int main() {
    char buffer[5]; // 声明一个足够大的字符数组

    // 使用 memset 初始化 buffer 的前 5 个字节为 '6'
    memset(buffer, '6', 5);

    // 打印
    printf("Initialized buffer with '6':\n");
    for (int i = 0; i < 5; ++i) {
        printf("%c ", buffer[i]);
    }

    return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MR_Promethus

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值