PHP笔记网

革命尚未成功,同志仍须努力下载JDK17

作者:Albert.Wen  添加时间:2015-10-12 10:07:37  修改时间:2024-11-13 10:58:23  分类:13.C/C++/Rust  编辑

realloc()函数用来为ptr重新分配大小为size的一块内存,看似很简单,在使用过程中却会发生各种错误。

函数形式为:

void * realloc ( void * ptr, size_t new_size );

最近在网上查看了一些资料(在glibc中没有找到具体的实现),查到了一个开源项目自己写的realloc代码,http://code.google.com/p/mallocspethmeniel/source/browse/trunk/realloc.c?r=23,应该和stdlib里面的实现流程差不多。

下面是我画出的函数流程图:

这个函数的工作流程:

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)

 

参考文章:

 

摘自:https://www.cnblogs.com/ladd/archive/2012/06/30/2571420.html

 


realloc() 示例

/***********************************
 * 用realloc调整数组长度
 ***********************************/

// 获取 命令行输入的字符串
char *get_line(void) {
    const size_t sizeIncrement = 10;
    char *buffer = malloc(sizeIncrement);
    char *currentPostion = buffer;
    size_t maximumLength = sizeIncrement;
    size_t length = 0;
    int character;

    if (currentPostion == NULL) {
        return NULL;
    }

    while (1) {
        character = fgetc(stdin);

        // 回车结束输入
        if (character == '\n') {
            break;
        }

        length++;
        if (length >= maximumLength) {
            maximumLength += sizeIncrement;
            char *newBuffer = realloc(buffer, maximumLength);

            if (newBuffer == NULL) {
                free(buffer);
                return NULL;
            }

            currentPostion = newBuffer + (currentPostion - buffer);
            buffer = newBuffer;
        }
        *currentPostion++ = (char) character;
    }
    
    *currentPostion = '\0';

    return buffer;
}


int main(int argc, char **argv) {

    char *buffer = get_line();
    printf("%s\n", buffer);
    free(buffer);

    return EXIT_SUCCESS;
}

代码解释

缓冲区创建时的大小是 sizeIncrement,如果 malloc() 函数无法分配内存,第一个 if 语句会强制 get_line() 函数返回 NULL。接着是一次处理一个字符的无限循环,循环退出后,字符串末尾会添加NULL,然后返回缓存区的地址。

在 while 循环内部,程序每次读入一个字符,如果是回车符,循环退出。接着,if 语句判断我们有没有超出缓存区大小,如果没有超出,字符就被添加到缓冲区中。

如果超出了缓冲区大小,realloc() 函数会分配一块新内存,这块内存比旧内存大 sizeIncrement 字节。如果无法分配内存,我们会释放现有的已分配内存,强制函数返回 NULL;否则 currentPostion 会调整为指向新分配的缓冲区。realloc() 函数不一定会让已有的内存保持在原来的位置,所以必须用它返回的指针来确定调整过大小的内存块位置。

newBuffer 变量持有已分配内存的地址,我们需要用别的变量而不是 buffer,这样万一 realloc() 无法分配内存,我们也可以检测到这种情况并进行处理。

如果 realloc() 分配成功,我们不需要释放 buffer,因为 realloc() 会把原来的缓冲区复制到新的缓冲区中,再把旧的释放。如果视图释放 buffer,十有八九程序会终止,因为我们试图重复释放同一块内存。

 

 

相关文章: