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

这篇具有很好参考价值的文章主要介绍了C++ 指针进阶:动态分配内存。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

malloc / free

工作原理

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

  • 作用

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

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

  • 参数_Size 表示所需分配内存的字节数;即需要显式填入申请内存的大小,如 n * sizeof(int)

  • 返回值

    malloc 返回一个指向分配内存的指针;如果分配失败,则 NULL 指针,可以通过返回值判断是否分配成功;

    返回指针的类型为 void*,需要显示类型转换;

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

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

  • 作用free 函数会将用户释放的内存块连接到空闲链上;

  • 参数:指针 _Memory 应指向由 malloc(), calloc(), realloc() 分配的内存块,其他方式声明的内存不能用 free()

  • free 函数的实参 ptr 必须指向内存块的首地址(即 malloc 的返回值),否则可能导致错误;

  • 动态分配的内存需要手动释放,否则可能导致内存泄漏;

  • 调用 free(ptr) 后,并不会改变 ptr 的值,但是最好将指针设置为 NULL 以避免悬空指针问题。

具体使用

动态创建一维数组

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));

需要注意,这样获得的二维数组,不能保证其空间是连续的。但是可以通过 arr[i][j] 的方式访问元素 (i, j).

释放二维数组时需要一个逆向操作:

for (int i = 0; i < m; i ++)
    free(arr[i]);
free(arr);

calloc

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

callocmalloc 的主要区别有:

  • 作用

    calloc 函数用于在程序运行时分配一块指定数量和大小的内存,并将每个字节的内容初始化为零;

  • 参数

    NumOfElements 为要分配的元素个数;

    _SizeOfElements 为每个元素的大小(以字节为单位);

  • 返回值

    返回值方式与 malloc 相同;

  • 由于初始化分配内存的原因,calloc 的运行效率要低于 malloc

realloc

realloc 定义在 stdlib.h 库中,声明为 void *__cdecl realloc(void *_Memory,size_t _NewSize);,用于对动态内存块进行扩大或缩小:

  • 原理:如果原内存块后有足够的连续空间,则会直接扩大原内存块;否则将数据拷贝到新分配的内存空间,并释放元内存块(自动释放,不需要 free);

  • 参数

    _Memory 为 指向之前分配内存块的指针;

    _NewSize 为 调整后的内存块的字节数;

  • 返回值

    如果 _Memory 后有足够的连续空间,则直接扩大原内存块,并返回 _Memory

    如果空间不够,则返回指向重新分配的内存块的指针;

    如果重新分配失败且 ptr 不为 NULL,则返回 NULL,并且原来的内存块保持不变;

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

  • 如果 _Memory 为空,则 realloc 相当于 malloc

new / delete

工作原理

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

  • 工作原理

    new 首先会分配所需大小的内存,并调用对象的构造函数来初始化对象;对于数组,new 会分配足够的内存来容纳整个数组,并返回数组的首地址;

    delete 会调用对象的析构函数进行清理工作,然后释放对象占用的内存;对于数组,delete[] 会正确的释放整个数组所占用的内存,并对数组中的每个函数调用析构函数;

  • 返回值

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

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

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

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

  • newdelete 支持 重载

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

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

  • 使用 new 分配内存后,必须使用 delete 释放内存,否则可能导致内存泄漏;

  • 不要对非 new 分配的内存使用 delete,也不要对 new 分配的内存使用 free

具体应用

动态实例化

// 动态创建变量
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) 的元素。

    由于指向数组的指针也是一种确定的数据类型,因此除数组的第一维外,其他维度的长度均须为一个能在编译器确定的常量。文章来源地址https://www.toymoban.com/news/detail-746961.html

malloc 和 new 的主要区别

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

到了这里,关于C++ 指针进阶:动态分配内存的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++与C语言动态内存管理的不同 new与malloc

      目录 1.C语言动态内存管理方式 2.C++中动态内存管理 2.1 new和delete操作内置类型 2.2 new和delete操作自定类型 2.3 为什么delete要带[ ]  3.new申请空间失败 4.operator new 与 operator delete 函数 5.new与delete的是实现原理 5.1 内置类型 5.2 自定义类型 6.定位new表达式(了解即可) 7.malloc/free和

    2024年02月08日
    浏览(30)
  • 8.8 【C语言】动态内存分配与指向它的指针变量

    栈:全局变量和局部变量,全局变量是分配在内存中的静态存储区的,非静态的局部变量是分配在内存中的动态存储区的。 堆:数据临时存放在一个特别的自由存储区。 对内存的动态分配是通过系统提供的库函数来实现的,主要有malloc,calloc,free,realloc这四个函数。 1.用mallo

    2024年02月11日
    浏览(32)
  • 16 malloc 虚拟内存分配的调试(1)

    呵呵 在 c 语言中 malloc 应该是初学者必须了解的一个函数了吧  但凡 涉及到堆内存分配的相关, 必定会使用到 malloc, realloc, calloc 这几个函数  其中 malloc 最常见, 也是最 实用  在 HotspotVM 中也经常会看到 malloc 的身影  我们这里 来调试一下 malloc 的相关的一些场景  本文主要的

    2023年04月09日
    浏览(28)
  • 从 malloc 分配大块内存失败 来简看 linux 内存管理

    应用进程 malloc 返回了null,但是观察到的os 的free内存还有较大的余量 ,很奇怪为什么会这样? 不可能是oom导致的(当然也没有 os 的oom 日志),free还有余量,系统也没有cgroup的应用隔离。 我们linux上使用的库函数 malloc 基本都是用glibc库实现的malloc函数(当然如果binary 链接

    2024年02月08日
    浏览(40)
  • C++——内存分配与动态内存管理

    🌸作者简介: 花想云 ,在读本科生一枚,致力于 C/C++、Linux 学习。 🌸 本文收录于 C++系列 ,本专栏主要内容为 C++ 初阶、C++ 进阶、STL 详解等,专为大学生打造全套 C++ 学习教程,持续更新! 🌸 相关专栏推荐: C语言初阶系列 、 C语言进阶系列 、 数据结构与算法 本章我们

    2023年04月17日
    浏览(45)
  • C++类和动态内存分配

    C++能够在程序运行时决定内存的分配,而不是只在编译阶段,因此,就可以根据程序的需要,而不是根据一系列严格的存储类型规则来使用内存,C++使用new和delete运算符来动态控制内存,但是,在类中使用这些运算符会导致许多新的问题,在这种情况下,析构函数就是必不可

    2024年04月16日
    浏览(33)
  • C++内存分配详解:栈、堆、静态存储区解析与实例演示

    概述: C++内存分配有栈、堆和静态存储区三种方式。栈自动管理,适用于局部变量;堆手动管理,使用new和delete;静态存储区适用于全局变量,具有整个程序生命周期。通过清晰的示例源代码,详细解释了它们的分配方法和使用步骤。 C++的内存分配涉及栈、堆和静态存储区

    2024年02月04日
    浏览(29)
  • Postgresql源码(110)分析dsm动态共享内存分配与共享内存mq实例(dsm/toc接口备忘录)

    相关 《Postgresql源码(90)共享内存申请CreateSharedMemoryAndSemaphores》 《Linux内存映射函数mmap与匿名内存块》 《Linux共享内存与子进程继承》 用dsm框架的流程 评估共享内存大小:多次用shm_toc_estimate_chunk、shm_toc_estimate_keys向estimate中增加数据结构,最后用shm_toc_estimate得出刚才增加

    2024年02月14日
    浏览(48)
  • 如何实现动态分配,malloc,realloc,calloc的使用方法,数组,链表,结构体实现动态分配(含代码实现)

    🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨,经典算法的解析✨都在这儿,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏 🪔本系列专栏 -  数据结构与算法_勾栏听曲_0 🍻欢迎大家  🏹  点赞👍  评论📨  收藏⭐️ 📌个人主

    2023年04月27日
    浏览(36)
  • 动态内存函数malloc,calloc,realloc详解

    🍍个人主页🍍:🔜勇敢的小牛儿🚩 🔱推荐专栏🔱:C语言知识点 ⚠️座右铭⚠️: 敢于尝试才有机会 🐒今日鸡汤🐒: 出色一点 从能力到容貌 目录 思维导图: 一,malloc : 1.1:malloc函数简介: 1.2:malloc函数的使用:  代码: 二,calloc函数 2.1calloc函数简介: 2.2calloc函数

    2024年02月04日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包