【问题标题】:cannot realloc a double pointer dynamic memory不能重新分配双指针动态内存
【发布时间】:2020-04-22 13:00:54
【问题描述】:

我试图在另一个方法/函数中增加一个双指针缓冲区。但是,分配的缓冲区的大小不会改变。这是我试过的代码。

#include <stdio.h>
#include <stdlib.h>

void change_buffer_size(char ** buffer)
{
     *buffer = realloc(*buffer, sizeof(char) * 2);
}

int main()
{
    char **buffer = malloc(sizeof(char) * 1); 
    *buffer[0] = 'H';
    change_buffer_size(buffer);
    *buffer[1] = 'i';
    printf("%s\n", *buffer);
    return 0;
}

我收到Segmentation fault (core dumped) 错误。

【问题讨论】:

  • 总是 realloc 带有一个临时指针。当realloc 失败时,它会返回NULL,这将覆盖您的原始指针,从而导致内存泄漏。例如。 void *tmp = realloc (*buffer * 2 * sizeof **buffer); if (!tmp) { perror ("realloc-tmp"); return; }; *buffer = tmp; }

标签: c memory malloc realloc


【解决方案1】:
char **buffer = malloc(sizeof(char) * 1); 

这是错误的。你现在有一个char**,所以它指向的是一个char*,但它里面只有足够的空间容纳char。你也只分配了一层双指针。

*buffer[0] = 'H';

由于上述问题,该行导致了segfault。它正在尝试在未定义的位置写入内存。

解决此问题的最佳方法是正常分配第一层,必要时使用&amp;,第二层仅使用malloc

另外,%s 在看到空字节之前不会停止写入,因此您需要分配并写入其中一个。以下是您如何解决所有问题的方法:

#include <stdio.h>
#include <stdlib.h>

void change_buffer_size(char ** buffer)
{
     *buffer = realloc(*buffer, sizeof(char) * 3);
}

int main()
{
    char *buffer = malloc(sizeof(char) * 1); 
    buffer[0] = 'H';
    change_buffer_size(&buffer);
    buffer[1] = 'i';
    buffer[2] = '\0';
    printf("%s\n", buffer);
    return 0;
}

【讨论】:

  • 非常感谢您的解决方案。您能告诉我如何修改change_buffer_size 函数中的缓冲区吗?例如,buffer[1] = 'i'; 在 realloc 之后。我再次遇到分段错误。
  • @vortex 请注意,buffer 现在在 change_buffer_sizemain 中是不同的类型。您在main 中写的内容是正确的,但在change_buffer_size 中,您需要改为(*buffer)[1] = 'i';
  • 再次感谢。您能否详细说明一下我为什么要使用这样的语法?
  • @vortex 因为buffer 是一个char** 那里,所以你需要先做(*buffer) 得到一个char*,然后[1] 得到一个char 你终于可以写'i'了。
  • 在将buffer 视为printf("%s\n", buffer); @ 中的字符串时,您仍然会调用Undefined Behavior - 缺少nul-terminating 字符987654347@ 太短了一个。
【解决方案2】:

@JosephSible 已经给你一个很好的答案,但还有一点需要说明realloc 的使用。我在原始问题下的评论中提到了这一点。使用realloc 时,您必须始终使用realloc 使用临时 指针来捕获realloc 的返回。

为什么?当(不是如果)realloc 失败时,它返回NULL。如果您将返回值直接分配给您尝试重新分配的指针,您将覆盖指向现有内存块的指针,NULL 失去对原始块的引用,从而造成内存泄漏。例如,您不想:

    *buffer = realloc (*buffer, 2);       /* sizeof(char) is always 1 */

相反,在将重新分配的块分配给原始指针之前,请使用临时指针并验证重新分配是否成功,例如

    void * tmp = realloc (*buffer, 2);
    if (!tmp) {     /* validate EVERY allocation & reallocation */
        perror ("realloc-*buffer");
        exit (EXIT_FAILURE);        /* or handle as desired, e.g return NULL, etc.. */
    }
    *buffer = tmp;  /* now assign the reallocated block to your pointer */

您原来的帖子上有几个 cmets。回想一下 C 中的字符串必须以 nul-terminating 字符结尾。您不能简单地分配buffer[0] = 'H'; 并将buffer 视为字符串。 nul-termianting 字符('\0',或简称为0)必须跟在后面,所以在调用printf("%s\n", *buffer);之前需要buffer[1] = 0;

避免在代码中使用 幻数。您的 change_buffer_size() 函数使用幻数 2 将重新分配硬编码为大小。 (不是很有用)。相反,至少将所需的大小作为参数传递,以便您的函数可重用,例如

char *change_buffer_size (char **buffer, size_t nchar)
{
    void *tmp = realloc (*buffer, nchar * sizeof **buffer);
    if (!tmp) { /* validate EVERY allocation/reallocation */
        perror ("change_buffer_size()-realloc");
        return NULL;
    }

    return *buffer = tmp;   /* assign new block to *buffer, return */
}

注意:将返回类型更改为char* 允许您通过返回指示函数的成功/失败以及成功时直接访问重新分配的内存块)

现在您想将缓冲区重新分配给 2 个字符,只需将 2 传递为 nchar 等。结合一个简短的示例,一次重新分配并添加一个字符到您的缓冲区(同时确保它总是 nul-terminated) 可能类似于以下内容:

#include <stdio.h>
#include <stdlib.h>

char *change_buffer_size (char **buffer, size_t nchar)
{
    void *tmp = realloc (*buffer, nchar * sizeof **buffer);
    if (!tmp) { /* validate EVERY allocation/reallocation */
        perror ("change_buffer_size()-realloc");
        return NULL;
    }

    return *buffer = tmp;   /* assign new block to *buffer, return */
}

int main (void) {

    size_t nchar = 1;   /* character counter */
    char *str = "hello world!", *buffer = NULL;

    for (int i = 0; str[i]; i++) {
        if (!change_buffer_size(&buffer, nchar + 1))    /* alloc nchar + 1 */
            return 1;
        buffer[nchar-1] = str[i];           /* copy char from str to buffer */
        buffer[nchar++] = 0;                /* nul-terminate buffer */
        printf ("buffer: '%s'\n", buffer);  /* print current buffer contents */
    }

    free (buffer);      /* don't forget to free what you allocate */
}

(注意:不要忘记free()你分配的内存。是的,在这里它会在程序退出时被释放,但要尽早养成好习惯——你不会一直在工作在main())

使用/输出示例

$ ./bin/realloccharbuf
buffer: 'h'
buffer: 'he'
buffer: 'hel'
buffer: 'hell'
buffer: 'hello'
buffer: 'hello '
buffer: 'hello w'
buffer: 'hello wo'
buffer: 'hello wor'
buffer: 'hello worl'
buffer: 'hello world'
buffer: 'hello world!'

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 个职责:(1)始终保留指向起始地址的指针内存块,因此,(2) 当不再需要它时可以释放

您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配块的边界,尝试读取或基于未初始化的值进行条件跳转,最后,以确认您释放了已分配的所有内存。

对于 Linux,valgrind 是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。

$ valgrind ./bin/realloccharbuf
==19740== Memcheck, a memory error detector
==19740== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==19740== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==19740== Command: ./bin/realloccharbuf
==19740==
buffer: 'h'
buffer: 'he'
buffer: 'hel'
buffer: 'hell'
buffer: 'hello'
buffer: 'hello '
buffer: 'hello w'
buffer: 'hello wo'
buffer: 'hello wor'
buffer: 'hello worl'
buffer: 'hello world'
buffer: 'hello world!'
==19740==
==19740== HEAP SUMMARY:
==19740==     in use at exit: 0 bytes in 0 blocks
==19740==   total heap usage: 13 allocs, 13 frees, 1,114 bytes allocated
==19740==
==19740== All heap blocks were freed -- no leaks are possible
==19740==
==19740== For counts of detected and suppressed errors, rerun with: -v
==19740== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认您已释放已分配的所有内存并且没有内存错误。

检查一下,如果您有任何问题,请告诉我。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-12
  • 2015-02-05
  • 2011-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多