关于内存分配函数realloc()的思考

关于内存分配函数realloc()的思考
ZWN
060115
这个问题有一个程序开始讲起:
Status Push (SqStack &s, SElemType a)
{
         //插入元素a为新的栈顶元素
         if (S.top – S.base>=S.stacksize) //栈满,追加存储空间
         {
                   S.base = (SElemType *) realloc( S.base,(S.stacksize+STACKINCREMENT)*sizeof(SElemType));
                   if (!S.base) exit(OVERFLOW); //存储空间分配失败
                   S.top=S.base+S.stacksize;
                   S.stacksize+=STACKINCREMENT;
         }
         *S.top++=a;
         return OK;
} //Push.
这段代码出自于《数据结构》(严蔚敏 吴伟民)第47页。函数的只要功能实现元素压栈,如果栈的空间不足够的时候利用realloc()函数进行再新分配空间,使得函数得以实现。
我们注意里边的一句代码:
S.top=S.base+S.stacksize;
在再分配空间之前,S.top指向栈的顶部。

而这句代码有是什么意思呢?S.top=S.base*S.stacksize同样是指向栈的顶部元素。这是不是多此一举呢?
我们看realloc()函数的一些说明。

语句:
realloc (ptr, size)
 
参数:
ptr is a null pointer or a pointer previously returned by calloc or malloc or realloc function, size is an integer representing the number of byes to be allocated.
 
返回值:
A pointer to the possibly moved allocated memory, if any.  Returns null otherwise
资料来自:http://www.picsupport.com/ccsc/REALLOC.htm
由这些资料我们可以看出,函数realloc()在使用的过程中,可能改变prt的值。如果要开辟的内存在原来的空间上不能够实现的时候,函数将在另外的地方开辟新的空间,复制数据,把原来的内存空间释放(free())返回新的内存地址。问题就是在这里了,回到我们原来的程序,如果在追加空间的时候改变了指针S.base的值,那么必须保证S.top也作响应的改变。
语句S.top=S.base+S.stacksize;保证了整个程序的安全性。
为了验证realloc()函数有可能改变指针地址,我们看下面的代码。
#include<string.h>
#include<malloc.h>
main()
{
       char *p,*q;
        p = (char *)malloc(10);
    strcpy(p,"abcdefghi/0");
    printf("%d/n",p);               //第一次malloc后p所指向的空间
    q = p;
    p = (char * )realloc(p,8521);
printf("%d    %s/n",p,p);
 //可以观察realloc后p指向的地址同malloc后所指向的地址是否发生
            //了变化,如果没有变化就把realloc中的第二个参数再变大一些!
         free(q);   //当p值得发生改变的时候,该语句将会发生错误,程序出错。因为q指向                                //的空间已经被realloc()函数释放,
       if(p==q)
              printf("p==q");
       else
              printf("p!=q");
       printf("/n %s",q);
       printf("/n %s",p);
}
在该段程序中,如果把语句free()注释掉,我们还可以看到:语句printf("/n %s",q);和printf("/n %s",p);打印的值得还是一样的,说明realloc()的释放空间功能并没有将数据删除,只是告诉编译器没在被分配的状态下。事实上,关于free()函数,有如下的说明:
The free function causes the space pointed to by the ptr to be deallocated, that is made available for further allocation. If ptr is a null pointer, no action occurs. If the ptr does not match a pointer earlier returned by the calloc, malloc or realloc, or if the space has been deallocated by a call to free or realloc function, the behavior is undefined.
资料来自:http://www.picsupport.com/ccsc/FREE.htm
显然我们可以了解到,为什么当p的值发生改变时,使得free(q)语句出错,同时更清楚知道free()函数的本质。
-------------------------------------------------------------------------------------------------------------------
 
说明:本文的写作参考了一写网上资料,提供上网址:
http://www.picsupport.com/ccsc/FREE.htm    关于free(prt)函数的使用
http://www.picsupport.com/ccsc/REALLOC.htm   realloc(prt,size)的使用。
http://dev.youkuaiyun.com/article/27/27950.shtm 内存分配——realloc()函数的使用
<think>好的,我现在需要帮助用户了解C++中的动态内存分配,特别是malloc、new和operator new的区别和使用方法。首先,我应该回顾用户提供的引用内容,确保信息准确。 用户提到了三个引用,其中引用1和2详细比较了malloc和new的区别,包括它们的行为、失败处理、构造函数的调用等。引用3则深入讨论了operator new的内部机制,以及new和delete的工作流程。根据这些信息,我需要组织一个结构清晰的教程,逐步解释动态内存分配的基础概念、使用方法、区别以及注意事项。 首先,应该从基础开始,介绍动态内存分配的必要性,比如在运行时确定内存需求的情况。然后分别讲解malloc/free和new/delete的语法和用法,用代码示例展示如何分配单个对象和数组。例如,用malloc分配int内存,并转换为正确类型,而new直接返回类型指针,更简洁安全。 接下来,比较两者的区别是关键部分。需要涵盖库函数与运算符的不同、构造/析构函数的调用、失败处理方式、内存来源(堆与自由存储区)以及类型安全。特别是类型安全这一点,malloc返回void*需要强制转换,而new直接返回正确类型,减少错误。 然后,引入operator new的概念,解释它是如何被new调用的,以及其与malloc的关系。这里可以引用引用3中的代码片段,说明operator new内部使用malloc,并在失败时抛出异常。同时,提到用户可以重载operator new来自定义内存分配策略,但要注意与构造函数的分离。 内存释放的部分需要强调匹配使用free/delete,避免内存泄漏和未定义行为。对于数组,要特别指出使用delete[]的重要性,确保每个元素的析构函数被调用。 在常见问题中,要回答用户可能遇到的疑问,比如何时用new代替malloc,内存泄漏的检测工具,以及处理分配失败的方法。例如,使用try-catch块捕获new的异常,或者检查malloc的返回指针。 最后,总结最佳实践,推荐在C++中优先使用new/delete,并保持分配和释放操作的一致性。同时,提醒用户注意数组处理的特殊性,避免错误。 整个过程中,需要确保符合用户给出的系统级指令,如正确使用LaTeX格式,生成相关问题,引用标识等。检查是否有遗漏的重要点,比如realloc的功能在C++中的替代方案,或者placement new等高级用法,但根据用户的需求,可能不需要深入这些细节,保持教程的基础性和实用性。 现在需要将以上思考整理成结构化的回答,确保每个部分清晰,代码示例正确,引用适当。同时,生成的相关问题要围绕用户可能进一步询问的方向,比如区别对比、异常处理、内存管理工具等。</think>### C++动态内存分配详解 #### 一、基础概念 动态内存分配允许程序在运行时按需申请内存空间。在C++中主要通过以下两种方式实现: 1. **C标准库函数**:$malloc/calloc/realloc/free$ 2. **C++运算符**:$new/new[]$ 和 $delete/delete[]$ #### 二、malloc与new的使用 1. **malloc/free示例**: ```cpp int* p = (int*)malloc(sizeof(int)*10); // 分配10个int空间 free(p); // 必须手动类型转换[^2] ``` 2. **new/delete示例**: ```cpp int* p = new int(5); // 分配单个int并初始化为5 delete p; // 自动调用析构函数 int* arr = new int[10]; // 分配10个int数组 delete[] arr; // 必须使用delete[] ``` #### 三、核心区别对比(表格) | 特性 | malloc/free | new/delete | |---------------------|---------------------------------|---------------------------------| | 语言标准 | C标准库函数 | C++运算符 | | 构造函数调用 | 不调用 | 自动调用[^1] | | 失败处理 | 返回NULL | 抛出bad_alloc异常[^2] | | 类型安全 | 需要强制类型转换 | 自动返回正确类型指针 | | 内存来源 | 堆内存 | 自由存储区(可能是堆)[^2] | | 重载支持 | 不支持 | 支持operator new重载[^3] | #### 四、new运算符底层原理 $$ new \ Type = operator\ new(sizeof(Type)) + Type::Type() $$ ```cpp // operator new底层实现示例 void* operator new(size_t size) { void* p = malloc(size); // 实际调用malloc[^3] if(p == nullptr) throw std::bad_alloc(); return p; } ``` #### 五、内存释放要点 1. **必须匹配释放**: - $malloc$分配 → $free$释放 - $new$分配 → $delete$释放 - $new[]$分配 → $delete[]$释放 2. **数组释放示例**: ```cpp class MyClass { public: ~MyClass() { /* 析构逻辑 */ } }; MyClass* arr = new MyClass[5]; delete[] arr; // 正确:调用5次析构函数 ``` #### 六、常见问题解决方案 1. **内存泄漏检测**: - 使用Valgrind、AddressSanitizer等工具 - 智能指针(unique_ptr/shared_ptr) 2. **异常处理**: ```cpp try { int* p = new int[1000000000LL]; } catch(const std::bad_alloc& e) { std::cerr << "内存不足:" << e.what(); } ``` 3. **自定义内存管理**: ```cpp // 重载operator new void* operator new(size_t size) { std::cout << "申请" << size << "字节内存"; return malloc(size); } ``` #### 七、最佳实践建议 1. 优先使用new/delete(类型安全、自动构造) 2. 数组操作始终使用new[]/delete[] 3. 建议使用智能指针管理资源 4. 避免混合使用malloc和delete
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值