malloc/new

malloc:

原型:extern void *malloc(unsigned int num_bytes);  

头文件:在TC2.0中可以用malloc.h或 alloc.h (注意:alloc.h 与 malloc.h 的内容是完全一致的),而在Visual C++6.0中可以用malloc.h或者stdlib.h。  

功能:分配长度为num_bytes字节的内存块  

返回值:如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。  

说明:关于该函数的原型,在旧的版本中malloc返回的是char型指针,新的ANSIC标准规定,该函数返回为void型指针,因此必要时要进行类型转换。  

相关函数:calloc、realloc、free、_alloca

函数声明

void *malloc(int size);  

说明:malloc 向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型的指针。

C,C++规定,void* 类型可以强制转换为任何其它类型的指针。  

从函数声明上可以看出。malloc 和 new 至少有两个不同: new 返回指定类型的指针,并且可以自动计算所需要大小。

比如:  

int *p;  

p = new int; //返回类型为int* 类型(整数型指针),分配大小为 sizeof(int);  

或:  

int* parr;  

parr = new int [100]; //返回类型为 int* 类型(整数型指针),分配大小为 sizeof(int) * 100;  

而 malloc 则必须由我们计算要字节数,并且在返回后强行转换为实际类型的指针。  

int* p;  

p = (int *) malloc (sizeof(int));  

第一、malloc 函数返回的是 void * 类型,如果你写成:

p = malloc (sizeof(int)); 

则程序无法通过编译,报错:“不能将 void* 赋值给 int * 类型变量”。所以必须通过 (int *) 来将强制转换。 

第二、函数的实参为 sizeof(int) ,用于指明一个整型数据需要的大小。如果你写成:  

int* p = (int *) malloc (1);  

代码也能通过编译,但事实上只分配了1个字节大小的内存空间,当你往里头存入一个整数,就会有3个字节无家可归,而直接“住进邻居家”!造成的结果是后面的内存中原有数据内容全部被清空。  

malloc 也可以达到 new [] 的效果,申请出一段连续的内存,方法无非是指定你所需要内存大小。  

比如想分配100个int类型的空间:  

int* p = (int *) malloc ( sizeof(int) * 100 ); //分配可以放得下100个整数的内存空间。

  

另外有一点不能直接看出的区别是,malloc 只管分配内存,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值将是随机的。

  除了分配及最后释放的方法不一样以外,通过malloc或new得到指针,在其它操作上保持一致。

函数的工作机制


  malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。

new:

  从堆中划分一块区域,动态创建一个类型的数据,最后返回该区域的指针。该数据类型可以是标准数据类型,也可以是用户自定义类型。数据使用完后,应调用delete运算符来释放动态申请的内存(在堆中)。


   (1)new可用来生成动态无名变量,

  如 

int *p=new int;  

int *p[10]=new int [10]; //动态数组的大小可以是变量或常量;而一般直接声明数组时,数组大小必须是常量  

又如:  

int *p1;  

double *p2;  

p1=new int(12);  

p2=new double [100];

  

  分别表示动态分配了用于存放整型数据的内存空间,将初值12写入该内存空间,并将首地址值返回指针p1;

  动态分配了具有100个双精度实型数组元素的数组,同时将各存储区的首地址指针返回给指针变量p2;

  

  对于生成二维及更高维的数组,应使用多维指针,以二维指针为例

  int **p=new int* [row]; //row是二维数组的行,p是指向一个指针数组的指针

  for(int i=0;i<10;i++)

  p=new int [col]; //col是二维数组的列,p是指向一个int数组的指针


  (2)使用完动态无名变量后应该及时释放,要用到 delete 运算符

  delete p; //释放单个变量  delete []p; //释放数组变量(不论数组是几维)

  相比于一般的变量声明,使用new和delete 运算符可方便的使用变量。

    更多复杂的,以后再说

 

<think>嗯,用户想了解频繁使用malloc/new和free/delete在C/C++中会导致的问题。这个问题很实际,因为内存管理不当确实会引发各种严重问题。 从用户提供的引用材料来看,已经涵盖了malloc/free和new/delete的关键区别:比如new/delete会调用构造/析构函数,而malloc/free只是单纯分配释放内存。不过用户更关注的是"频繁使用"带来的问题,这需要更深入的分析。 首先想到的是内存碎片问题。频繁申请释放不同大小的内存块,会在堆中留下很多"空隙"。就像抽屉里乱塞大小不一的盒子,虽然总空间够用,但可能找不到连续空间存放一个大物件。引用[1][3]提到堆内存管理时没有涉及碎片细节,但这确实是高频操作的典型后果。 然后是性能开销。每次malloc/new都需要在堆中搜索合适的内存块,就像在杂乱仓库里找东西。特别是当内存碎片严重时,这个搜索时间会明显增加。引用[5]提到C++有自己的内存管理方式,但没具体说性能影响。 内存泄漏的风险也值得强调。虽然引用[1][2]都提到要配对使用,但实际编程中很容易漏掉delete,尤其是异常发生时。比如在newdelete之间抛出异常,如果没有用RAII包装,那块内存就永远泄漏了。 还有个隐藏问题是缓存局部性下降。频繁申请零散内存会导致数据在物理内存中分散存放,CPU缓存命中率降低。这点所有引用材料都没提到,但对性能影响很大。 用户似乎有C++基础,知道malloc/free和new/delete的区别。可以结合引用[4]的例子,说明内置类型虽然可以混用,但类类型绝对不行——用free释放new出来的对象会导致析构函数不被调用。 最后解决方案部分,引用[3][5]提到了智能指针和内存池,可以展开说说。特别是现代C++的unique_ptr如何自动管理生命周期,比手动delete可靠得多。 要不要补充调试工具呢?像valgrind能检测内存泄漏,但可能超出问题范围。还是集中在问题本身和核心解决方案上比较稳妥。 </think> 在C/C++程序中频繁使用`malloc/new`和`free/delete`进行手动内存管理可能导致以下关键问题,结合引用内容分析如下: --- ### **1. 内存碎片化** - **内部碎片**:分配的内存块大于实际所需(如对齐要求),造成空间浪费[^3]。 - **外部碎片**:频繁分配释放不同大小的内存块后,剩余内存被分割成零散小空间,导致后续无法分配连续大内存块,即使总空闲内存充足[^1][^5]。 > **影响**:降低内存利用率,可能触发操作系统频繁换页,拖慢程序性能。 --- ### **2. 内存泄漏** - **显式泄漏**:`malloc/new`分配内存后忘记调用`free/delete`释放[^3][^5]。 - **隐式泄漏**:异常或分支跳转导致释放代码未执行(需配合RAII解决)。 > **后果**:程序内存占用持续增长,最终可能因耗尽内存而崩溃。 --- ### **3. 未定义行为与崩溃** - **双重释放(Double Free)**: 同一块内存被多次释放,导致堆结构破坏(如`free`或`delete`同一指针两次)[^2]。 - **野指针(Dangling Pointer)**: 释放内存后未置空指针,后续误用该指针访问已释放内存[^1][^5]。 - **分配释放方式不匹配**: - 用`free`释放`new`创建的对象 → **跳过析构函数**,若对象持有资源(如文件句柄)则泄漏[^3]。 - 用`delete`释放`malloc`申请的内存 → **行为未定义**(编译器可能添加额外元数据)[^1][^2]。 > **示例**: > ```cpp > class Resource { ~Resource() { /* 释放资源 */ } }; > Resource* obj = new Resource(); > free(obj); // 错误!未调用析构函数,资源泄漏 > ``` --- ### **4. 性能开销** - **系统调用频繁**:每次`malloc/new`可能触发内核级内存分配(如通过`brk`或`mmap`),上下文切换开销大。 - **分配器算法开销**:内存管理器需搜索合适空闲块,碎片严重时效率更低。 --- ### **5. 构造/析构语义缺失** - **`malloc/free`的局限**: 仅分配/释放原始内存,**不调用构造函数和析构函数**,对C++对象不安全[^4]。 - **`new/delete`的必需性**: `new`调用构造函数,`delete`调用析构函数,确保对象生命周期正确[^3]。 > **错误示例**: > ```cpp > int* arr = (int*)malloc(10 * sizeof(int)); // 内置类型可用,但类类型危险 > MyClass* obj = (MyClass*)malloc(sizeof(MyClass)); // 构造函数未执行! > ``` --- ### **6. 类型安全与代码可读性** - **`malloc`需手动转换类型**:返回`void*`需强转,易出错[^3]。 - **`new`自动类型推导**:返回正确类型指针,减少错误。 > **对比**: > ```cpp > int* p1 = (int*)malloc(sizeof(int)); // C风格,繁琐 > int* p2 = new int; // C++风格,简洁安全 > ``` --- ### ⚠️ **根本解决思路**(结合引用建议) 1. **使用智能指针**(`std::unique_ptr`, `std::shared_ptr`): 自动管理生命周期,避免泄漏和双重释放[^3]。 2. **容器替代裸内存**: 优先使用`std::vector`, `std::string`等,避免手动分配数组。 3. **RAII原则**: 将资源封装在对象中,依赖析构函数自动释放。 4. **内存池定制**: 高频分配场景可定制分配器,减少碎片和系统调用。 --- ### 关键区别总结(引用自[^3][^4]) | 特性 | `new`/`delete` | `malloc`/`free` | |---------------------|-------------------------|-----------------------| | **语言** | C++ | C | | **构造/析构调用** | ✓ | ✗ | | **类型安全** | 自动推导类型 | 需手动强转`void*` | | **失败行为** | 抛出`std::bad_alloc` | 返回`NULL` | | **数组支持** | `new[]`/`delete[]` | 需手动计算大小 | > **核心原则**:C++中**禁止混用**`new`与`free`或`malloc`与`delete`,必须严格配对使用[^2]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值