Skip to content

内存相关

new 和 malloc 的区别

  1. 来源和设计目的

    • new: C++ 的表达式,分配的内存来自自由存储区
    • malloc: C 语言的标准库函数,分配的内存也来自堆
  2. 返回类型

    • new: 返回适当类型的指针。
    • malloc: 总是返回 void*,因此需要显式类型转换。例如:int* ptr = (int*)malloc(sizeof(int));
  3. 内存分配大小

    • new: 基于类型信息自动确定大小。
    • malloc: 需要手动指定字节数。
  4. 构造和析构

    • new: 在分配内存后,还会调用对象的构造函数(如果适用)。
    • malloc: 仅分配内存,不调用构造函数或进行初始化。
  5. 错误处理

    • new: 默认情况下,内存分配失败会抛出 std::bad_alloc 异常。但可以使用 new(std::nothrow) 返回 nullptr 而不抛出异常。
    • malloc: 内存分配失败时返回 NULL
  6. 释放内存

    • new: 使用 delete
    • malloc: 使用 free
  7. 数组和内存分配

    • new[]: 为数组分配内存,并调用其元素的构造函数。释放时使用 delete[].
    • malloc: 分配数组时需要手动计算字节数。释放时使用 free.
  8. 扩展性和灵活性

    • new: 在 C++ 中可以重载,允许自定义内存分配策略。
    • malloc: 是标准库函数,不可重载。
  9. 兼容性

    • new 是 C++ 特有的。
    • malloc 可在 C 和 C++ 中使用。
  10. 直观地重新分配内存

    • new 没有直观的配套设施来扩充内存
    • malloc: 如果在使用过程中发现内存不足,可以使用 realloc 函数进行内存重新分配实现内存的扩充。
      realloc 先判断当前的指针所指内存是否有足够的连续空间,如果有,原地扩大可分配的内存地址,并且返回原来的地址指针;如果空间不够,先按照新指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来的内存区域。
  11. 底层实现

    • new 调用的用于分配内存的 operator new 一般用 malloc 实现
    • malloc 无法用 new 实现

new 相关的底层函数?

new 底层实现过程
1. 内存被分配(通过 operator new 函数) 2. 为被分配的内存调用一个或多个构造函数

operator new 底层所做的工作
1. 调用 set_new_handler 函数,输入参数为 X 的出错处理函数。使得 X 的 new_handler 函数成为全局 new_handler 函数。(标准 set_new_handler函数存在于 std 空间)。
2. 调用全局的 operator new 分配内存。如果一次分配失败,全局的 operator new 会调用 X 的 new_handlernew_handler 函数是全局的),如果全局的 new_handler 最终未能分配到内存, new_handler 抛出 std::bad_alloc 异常,X 的 operator new 会捕捉到它。 X 的 operator new 然后会恢复最初被取代的全局 new_handler 函数,最后以抛出异常返回。
3. 假设全局 operator new 为类型 X 的对象分配内存成功,X 的 operator new 会再次调用标准 set_new_handler 函数来恢复最初的全局出错处理函数,最后返回分配成功的内存的指针。

operator new 内部包含一个无限循环,跳出循环的办法有4种,分别为:
1. 得到了更多的可用内存
2. 安装了一个新的 new_handler(出错处理函数)
3. 卸除了 new_handler,抛出了一个 std::bad_alloc 或其派生类型的异常
4. 返回失败

operator new 在无法完成内存分配请求时,会在抛出异常之前调用客户指定的一个出错处理函数 new_handler 函数。

malloc 相关的底层?

https://zhuanlan.zhihu.com/p/483719414
https://mp.weixin.qq.com/s/pdv5MMUQ9ACpeCpyGnxb1Q

堆栈溢出的原因?

堆栈溢出是指当栈内存使用超过其为其分配的容量时发生的一种错误。以下是导致堆栈溢出的一些常见原因:

  1. 递归无限循环:这是最常见的堆栈溢出原因之一。当一个函数递归调用自己并且没有适当的终止条件(或者终止条件没有正确设置)时,会导致无限的函数调用,每次调用都会将新的帧压入栈,直到栈耗尽。
  2. 过大的栈变量:局部变量存储在栈上。如果你在一个函数中分配了一个非常大的局部数组或其他数据结构,可能会超出栈的容量。
  3. 深层嵌套的函数调用:即使没有递归,如果有一系列的函数调用另一个函数,这样的深层嵌套也可能耗尽栈空间。
  4. 动态堆栈分配:某些操作系统或编程环境允许动态地改变栈的大小。如果程序或其库不当地设置了栈大小,可能导致溢出。
  5. 动态申请空间使用之后没有释放:由于没有垃圾资源自动回收机制,因此,需要程序主动释放已经不再使用的动态地址空间。
  6. 返回地址篡改:由于缓冲区溢出或其他类型的攻击,恶意代码可能尝试修改存储在栈上的返回地址,从而可能引发堆栈溢出错误。
  7. 使用非标准或不正确的调用约定:如果编译器、链接器或运行时期望一种调用约定,而实际代码使用了另一种,可能会导致不正确的栈操作,从而导致堆栈溢出。
  8. 线程栈大小:在多线程程序中,每个线程通常都有自己的栈。如果线程的栈大小设置得太小,那么该线程可能会经历堆栈溢出,即使主线程或其他线程的栈还有很多可用空间。

栈的大小的确定?一个函数的栈的大小取决于什么?函数栈是在什么期间确定?

https://blog.csdn.net/holybin/article/details/37344671

https://www.zhihu.com/question/300116453

https://stackoverflow.com/questions/1825964/c-c-maximum-stack-size-of-program-on-mainstream-oses