realloc 使用详解(分析realloc invalid pointer、指针无效等错误)

本文详细解析了realloc函数的工作原理及使用注意事项。重点介绍了realloc如何为已分配的内存改变大小,包括当新大小小于原有大小时可能导致的数据丢失问题以及新内存分配后的指针更新策略。
http://www.cnblogs.com/ladd/archive/2012/06/30/2571420.html
realloc函数用来为ptr重新分配大小为size的一块内存,看似很简单,在使用过程中却会发生各种错误。

函数形式为:
void * realloc ( void * ptr, size_t new_size );
最近在网上查看了一些资料,在glibc中没有找到具体的实现(有人找到了可以发给我,ladd.cn@gmail.com),查到了一个开源项目自己写的realloc代码,http://code.google.com/p/mallocspethmeniel/source/browse/trunk/realloc.c?r=23,应该和stdlib里面的实现流程一样。
下面是我画出的函数流程图

realloc 函数流程图

这个函数的工作流程:
1.对ptr进行判断,如果ptr为NULL,则函数相当于malloc(new_size),试着分配一块大小为new_size的内存,如果成功将地址返回,否则返回NULL。如果ptr不为NULL,则进入2
2.查看ptr是不是在堆中,如果不是的话会跑出异常错误,会发生realloc invalid pointer(具体原因在后面讲)。如果ptr在堆中,则查看new_size大小,如果new_size大小为0,则相当于free(ptr),讲ptr指针释放,返回NULL,如果new_size小于原大小,则ptr中的数据可能会丢失,只有new_size大小的数据会保存(这里很重要),如果size等于原大小,等于啥都没做,如果size大于原大小,则看ptr所在的位置还有没有足够的连续内存空间,如果有的话,分配更多的空间,返回的地址和ptr相同,如果没有的话,在更大的空间内查找,如果找到size大小的空间,将旧的内容拷贝到新的内存中,把旧的内存释放掉,则返回新地址,否则返回NULL。
 
这里解释一下,为什么ptr要在堆中,学过编译原理的同学应该都知道C语言中的malloc,realloc,calloc()这种用户主动分配的内存放在堆区,而临时变量放在栈区,这是两块不同的区域。堆区的变量由用户通过free释放空间,而栈区的变量在函数退出后就自动释放,所以这两个区域的变量生存时间不同,不能相互间进行内存分配操作。所以要求ptr必须在堆区,即ptr必须是malloc,realloc或者calloc的返回值,要不然可以为NULL,把realloc变为malloc。
所以使用realloc函数应该注意一下几点:
1.ptr必须为NULL,或者为malloc,realloc或者calloc的返回值,否则发生realloc invalid pointer错误
2.new_size如果小于old_size,只有new_size大小的数据会被保存,可能会发生数据丢失,慎重使用。
3.如果new_size大于old_size,可能会分配一块新的内存,这时候ptr指向的内存会被释放,ptr成为野指针,再访问的时候会发生错误。
4.最后不要将返回结果再赋值给ptr,即ptr=realloc(ptr,new_size)是不建议使用的,因为如果内存分配失败,ptr会变为NULL,如果之前没有将ptr所在地址赋给其他值的话,会发生无法访问旧内存空间的情况,所以建议使用temp=realloc(ptr,new_size)。
参考文章:
1.function realloc  http://www.cplusplus.com/reference/clibrary/cstdlib/realloc/
2.realloc.c http://code.google.com/p/mallocspethmeniel/source/browse/trunk/realloc.c?r=23

以上内容为ladd原创,画流程图花了老长时间,如果转载请注明出处:
http://www.cnblogs.com/ladd/archive/2012/06/30/2571420.html 
<think>我们正在处理一个关于realloc函数导致的无效指针错误的问题。用户提供了几个引用,但引用内容与realloc的直接关联不大。不过,我们可以从内存管理的角度来分析这个问题。无效指针错误通常是由于以下原因之一引起的:1.传递给realloc指针不是由malloc、calloc或realloc等动态内存分配函数返回的。2.传递给realloc指针已经被释放(即悬垂指针)。3.传递给realloc指针被修改过(例如,指针运算后没有正确还原)或者指针本身是无效的。4.多线程环境中,另一个线程修改了指针。根据用户错误信息中的地址0x000e5338,这个地址可能是realloc试图重新分配内存的指针。我们需要检查这个指针的合法性。解决方案步骤:1.确认指针来源:确保指针最初是由内存分配函数分配的。例如:```cint*ptr=malloc(100*sizeof(int));//然后可以对这个ptr使用realloc```2.避免对已释放的指针使用realloc:在释放指针后,将指针设置为NULL,这样如果再次使用就会立即出错(因为realloc(NULL,size)相当于malloc(size)),但更关键的是不要使用已释放的指针。```cfree(ptr);ptr=NULL;//避免悬垂指针```3.检查realloc的返回值:realloc可能失败,返回NULL,但原来的内存块仍然有效。因此,不能直接覆盖原指针:```cint*new_ptr=realloc(ptr,new_size);if(new_ptr==NULL){//处理错误,原来的ptr仍然指向之前的内存,但这里要注意,如果realloc失败,我们通常需要保留原内存并处理//不要直接使用ptr=realloc(ptr,new_size);因为如果失败,ptr会变为NULL,原来的内存泄露且无法释放}else{ptr=new_ptr;}```4.避免指针被修改:确保传递给realloc指针没有被指针运算修改。例如:```cint*ptr=malloc(100*sizeof(int));int*ptr2=ptr+10;//现在ptr2不是一个基指针//不能使用realloc(ptr2,...);因为ptr2不是分配函数的直接返回值```5.在多线程环境中,确保在操作指针使用互斥锁,避免其他线程同时修改或释放该指针。6.使用内存调试工具:如Valgrind(在Linux下)或AddressSanitizer(gcc或clang的-fsanitize=address选项)来检测内存错误。7.检查内存越界:因为如果写越界,可能会破坏内存分配器的内部结构,导致realloc时出现无效指针错误。8.另外,用户引用[1]中提到“custodialpointer”的概念,即唯一指向分配内存的指针。在realloc时,我们需要保证传递的指针是custodialpointer(即没有被复制而失去唯一性?)实际上,realloc要求传递的指针必须是分配函数返回的,且没有被其他指针复制覆盖(注意:复制指针本身不会使原指针无效,但必须保证原指针没有被释放,并且仍然是分配函数返回的那个地址)。所以引用[1]中的说明并不直接适用于realloc,但关于指针唯一性的概念可以参考。我们应确保在realloc时,传递的指针仍然是分配函数返回的基指针。9.引用[2]提到了内存分配成功检查,同样适用于realloc:如果内存分配失败(返回NULL),我们应该有错误处理。10.引用[3]和[4]看起来与网络地址和DRM有关,似乎与当前问题无关。因此,针对用户的问题,我们建议:-检查传递给realloc指针是否是动态分配内存的基指针(没有进行过指针运算)。-检查该指针是否已经被释放。-检查多线程同步。-使用内存调试工具定位问题。-在realloc使用临时变量接收返回值,避免原指针被覆盖为NULL。如果用户能提供具体的代码片段,我们可以进一步分析。相关问题:1.如何避免动态内存分配中的悬垂指针?2.在多线程环境中如何安全地使用realloc?3.如何检测内存越界导致的动态内存分配器损坏?4.Valgrind和AddressSanitizer工具在内存调试上有何异同?</think> ### realloc导致的无效指针错误解决方案 `realloc`函数引发的无效指针错误(如您提到的`0x000e5338`)通常是由于内存管理不当造成的。结合您的引用和内存管理原理,以下是根本原因分析和解决方案: #### 核心问题原因(引用[1][2][4]关联) 1. **非托管指针操作** - 原始指针在`realloc`前已被其他操作覆盖或部分释放,违反了指针唯一性原则[^1] *示例错误代码:* ```c char *ptr = malloc(100); char *ptr2 = ptr; // ptr失去托管权[^1] ptr = realloc(ptr, 200); // 错误:ptr此时无效 ``` 2. **未验证内存分配成功** - `realloc`返回NULL时未中止操作[^2] ```c int *arr = malloc(10*sizeof(int)); // 未检查arr是否NULL就直接使用... arr = realloc(arr, 20*sizeof(int)); // 可能传递无效地址 ``` 3. **内存边界破坏** - 原内存区域被越界写入损坏堆结构 ```c int *arr = malloc(10*sizeof(int)); arr[10] = 42; // 越界写入破坏堆管理结构 arr = realloc(arr, 15*sizeof(int)); // 触发SIGABRT ``` #### 解决方案 1. **正确指针托管模式** ```c void *safe_realloc(void **ptr, size_t new_size) { void *tmp = realloc(*ptr, new_size); if(!tmp && new_size > 0) { fprintf(stderr, "内存分配失败\n"); return NULL; } *ptr = tmp; // 原子性更新指针[^1][^4] return tmp; } // 调用示例: int *arr = malloc(10); safe_realloc((void**)&arr, 20); ``` 2. **边界安全防护** - 使用Valgrind检测越界写入: `valgrind --tool=memcheck ./your_program` - 添加内存隔离区: ```c #define SAFE_MALLOC(size) malloc(size + 32) // 增加保护边界 ``` 3. **资源申请状态验证(DRM模型参考[^4])** ```c if((new_ptr = realloc(old_ptr, newsize)) == NULL) { // 保持原内存有效[^4] log_error("Realloc failed: %p", old_ptr); return old_ptr; // 不破坏原有状态 } ``` #### 调试定位步骤 1. 使用`backtrace()`获取调用栈: ```c #include <execinfo.h> void print_trace() { void *buffer[50]; int n = backtrace(buffer, 50); backtrace_symbols_fd(buffer, n, STDERR_FILENO); } ``` 2. 地址解析(参考引用[3]): ```shell addr2line -e your_program 0x000e5338 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值