首页 > 系统相关 >C++ 指针进阶:动态分配内存

C++ 指针进阶:动态分配内存

时间:2023-11-23 23:23:47浏览次数:21  
标签:malloc 进阶 int C++ 分配内存 内存 数组 new delete

C++ 动态实例化(new 和 malloc)

目录

malloc / free

工作原理

mallocstdlib.h 库中的函数,声明为 void *__cdecl malloc(size_t _Size);

  • malloc 函数沿空闲链表(位于内存 堆空间 中)申请一块满足需求的内存块,将所需大小的内存块分配给用户剩下的返回到链表上;

    并返回指向该内存区的首地址的指针,意该指针的类型为 void *,因此我们需要强制转换指针类型;

  • _Size 为要申请的空间大小,即需要显式填入申请内存的大小,如 n * sizeof(int)

  • malloc 并不会初始化所申请的空间;

  • malloc 分配内存失败时返回 NULL 指针,可以通过返回值判断是否分配成功;

free 也是 stdlib.h 库中的函数,声明为 void __cdecl free(void *_Memory);

  • free 函数会将用户释放的内存块连接到空闲链上;
  • 指针 _Memory 应指向由 malloc() 分配的内存块,其他方式声明的内存不能用 free()

具体使用

动态创建一维数组

size_t element_cnt = 10;
int *arr = (int *)malloc(element_cnt * sizeof(int));
free(arr)

动态创建二维数组

size_t m = 10, n = 10;
int **arr = (int **)malloc(m * sizeof(int *));
for (int i = 0; i < m; i ++)
    arr[i] = (int *)malloc(n * sizeof(int));

需要注意,这样获得的二维数组,不能保证其空间是连续的。

calloc

calloc 定义在 stdlib.h 库中,声明为 void *__cdecl calloc(size_t _NumOfElements,size_t _SizeOfElements);

callocmalloc 的主要区别有:

  • calloc 多了一个参数 NumOfElements,无需人为计算空间大小;

  • calloc 函数会将申请的空间逐一初始化为 0

    由于自动初始化的原因,calloc 的运行效率要低于 malloc

realloc

realloc 定义在 stdlib.h 库中,声明为 void *__cdecl realloc(void *_Memory,size_t _NewSize);,用于对动态内容进行扩容

  • 指针 _Memory 为 指向原来空间的指针;

    _NewSize 为 扩容后空间大小

  • 如果 _Memory 后有足够的连续空间,则扩大 _Memory 指向的地址,并返回 _Memory

    如果空间不够,则按照 _NewSize 分配空间,拷贝原有数据到新分配的内存空间,并释放 _Memory 所指的内存区(自动释放,不需要 free),同时返回新分配的内存区域的首地址;

  • 分配失败时返回空指针 NULL

  • _NewSize 小于原大小,原数据末尾可能会丢失;

new / delete

工作原理

newdelete 是 C++ 中的关键字,若要使用,需要编译器支持。

  • new 无需显式填入申请的内存大小,new 会根据 new 的类型分配内存;

  • new 分配的内存空间在自由存储区;

  • newdelete 支持重载;

  • 内存分配成功时,new 返回对象类型的指针,类型严格与对象匹配;

    new 内存分配失败时,会抛出 bac_alloc 异常,如果不捕捉异常,那么程序就会异常退出;

  • 不能对一块内存释放两次或以上;

    对空指针 nullptr 使用 delete 操作时合法的;

具体应用

动态实例化

// 动态创建变量
int *p = new int(1234);
/* ... */
delete p;

new 动态创建对象时会经历三个步骤:

  1. 调用 operator new 函数分配一块足够的内存空间(通常底层默认使用 malloc 实现)以存储特定类型的对象;
  2. 编译器运行相应的构造函数以构造函数,并为其传入初值;
  3. 返回一个指向该对象的指针;

delete 释放对象内存时会经历两个步骤:

  1. 调用对象的析构函数;
  2. 编译器调用 operator delete 函数释放内存空间(通常底层默认使用 free 实现);
// 开辟新的对象
class A {
    int a;
public:
    A(int a_) : a(a_) {}
};

int main() {
    A *p = new A(1234);
    /* ... */
    delete p;
}

{} 运算符可以用来初始化没有构造函数的结构。初次以外,使用 {} 运算符可以使得变量的初始化形式变得同意。

struct ThreeInt {
  int a;
  int b;
  int c;
};

int main() {
  ThreeInt* p = new ThreeInt{1, 2, 3};
  /* ... */
  delete p;
}

动态创建数组

创建和释放数组需要使用 new[]delete[]new[] 运算符会返回数组的首地址。

size_t element_cnt = 5;
int *p = new int[element_cnt];
/* ... */
delete[] p;

动态创建二维数组

动态创建二维数组有以下三种方式:

  1. 声明一个长度为 N * M 的一维数组,通过下标 r * M + c 访问二维数组中小标为 (r, c) 的元素:

    int *arr = new int[N * M];
    

    这种方法可以保证二维数组的物理空间是 连续的

  2. 通过变量存储 数组的数组 的首地址——指向一个一维数组的指针的地址。这个变量即 二重指针

    int **arr = new int*[M];
    for (int i = 0; i < M; i ++)
        a[i] = new int[N];
    

    需要注意,这样获得的二维数组,不能保证其空间是连续的。

    对于这样获得的内存的释放,需要进行一个逆向操作:想释放每一个一维数组,再释放存储一维数组首地址的地址:

    for (int i = 0; i < M; i ++)
        delete[] arr[i];
    delete[] arr;
    
  3. 第三种方法用到 指向数组的指针

    int (*arr)[N] = new int[M][N];
    /* ... */
    delete[] arr;
    

    这种方式得到的也是连续的内存,但与第一种方式相比,可以直接使用 arr[n] 的形式得到数组第 n + 1 行的首地址,使用 arr[r][c] 的形式访问到下标为 (r, c) 的元素。

    由于指向数组的指针也是一种确定的数据类型,因此除数组的第一维外,其他维度的长度均须为一个能在编译器确定的常量。

malloc 和 new 的主要区别

特征 new / delete malloc / free
分配内存的位置 自由存储区
内存分配失败 抛出异常 bac_alloc 返回 NULL
分配内存大小 编译器根据类型计算得出 显式指定字节数
处理数组 new[] 人为计算数组大小后进行内存分配
已分配内存的扩张 不支持 realloc
分配时内存不足 可以指定处理函数或重新指定分配器 无法通过用户代码处理
是否可以重载 可以 不可以
构造和析构函数 调用 不调用

标签:malloc,进阶,int,C++,分配内存,内存,数组,new,delete
From: https://www.cnblogs.com/buzzhou/p/17852750.html

相关文章

  • c++本质:释放内存、new与delete、容器内是指针
    【释放内存】本质:标识符放弃对该内存的占有权。若该内存是栈内存,当所有标识符都放弃,那么系统自动重获占有权。内存依然存在,地址、值都未改变。若该内存是堆内存,当所有标识符都放弃,不delete,那么系统也无法拥有占有权。所以delete让系统重获占有权。内存依然存在,地址未变、值变为......
  • UE4_C++实现TimeLine
    主要实现蓝图节点中时间轴的功能。目前UE提供了两种实现方式,一个是使用FTimeLine其是一个时间轴的结构体;另一种方式是使用UTimeLineComponent,其是一个时间轴组件类。两者内部定义的函数基本一样,组件类中使用这个结构体变量作为类中的成员变量。声明一个时间轴变量/组件FTimeL......
  • Kubernetes进阶之使用二进制包部署集群
    前言之前关于Kubernetes有写过文档参考:Kubernetes入门进阶课程https://www.cnblogs.com/minseo/category/1654539.html本文针对操作系统以及软件的新版本补充使用二进制包部署集群之前版本部署参考:https://www.cnblogs.com/minseo/p/12361731.html......
  • C++程序编译常见错误与评测各个状态含义
    编译常见错误提示1.[Error]expected';'before'cout'。在cout前面,缺少一个分号。2.[Error]'b'wasnotdeclaredinthisscope。未定义变量名b。3.[Error]stray'\243'inprogram\stray'\273'inprogram。不可识别的非法字符。4.[Error]ldretu......
  • 引发C++异常的常见原因(一)从报错地址到错误症状
    在进行C++软件开发的过程中,会遇到很多问题,网上差不到,或者查到了也没什么信息可以用,所以这里想到了就将一些常见的问题放在一起,归纳整理一下。本文主要的内容来源于CSDN的大佬文章:https://blog.csdn.net/chenlycly/article/details/125529931,我主要是做个笔记常见问题1.变量未......
  • C++11线程传递参数汇总
    一、概述总结C++11线程传递参数:1.传递基本数据类型(int、double)2.传递字符串3.传递结构体4.传递类对象二、代码示例//导入线程头文件#include<thread>//导入std命名空间usingnamespacestd;//传递int类型的参数voidthreadFu......
  • c++小程序/随机产生100以内的一个自然数,给出7次机会猜测数的大小
    一、随机产生100以内的一个自然数,给出7次机会猜测数的大小要求:1、 如果猜对了,提示:“真聪明,您猜对了!”,并退出程序2、 如果猜得数比随机数大,给出提示“你猜的数太大了”3、 如果猜得数比随机数小,提示“您猜的数太小了”,如果超出七次没有猜对,提示“很遗憾,您没有猜对”,并退出程序......
  • c++小程序/类Date
    声明类Date要求:1、使用户可以设置日期,并在输入错误时提示。a一年12个月,若输入12以外的数,提示“输入月份错误”b每个月依次有{31,28/29,31,30,31,30,31,31,30,31,30,31}若输入的数值超过该月天数,提示“输入天数错误”。2、可以查询两个日期之间相差天数注意:闰年Code#include<iostr......
  • c++小程序/数组排序
    数组排序1、定义交换两个整型数的函数swap2、定义sort函数给数组排序,调用swap函数Code#include<iostream.h>template<classT>voidswap(T&x,T&y){Ttemp;temp=x;x=y;y=temp;}template<classp>voidsort(p*a,pb){for(inti=0;i<10;i++){......
  • c++小程序/输入三个数,判断能不能组成三角形
    输入三个数,判断能不能组成三角形要求:1、输入-1,0,-1,提示“你输入的为负数”2、输入1,2,3,提示“你输入的三个数不能组成三角形”3、输入3,4,5提示“输入的三个数能组成三角形,并且是一个直角三角形”4、输入3,3,4提示“输入的三个数能组成三角形,并且是一个等腰三角形”1、 输入3,3,3提示“输......