realloc 无法重新分配先前 malloc 的指针

realloc fails to reallocate a previously malloc-ed pointer

我正在开发一个函数,使用 getchar()stdin 中的整行读入 char* 中,它大部分都有效,但是当我输入更长的字符串时,我得到

realloc(): invalid next size: 0x00000000007ca010

函数如下:

char *readCMDLine() {
    char *cmdline = malloc(sizeof(char));
    int counter = 0;
    char c;
    while (c != 10) {
        c = getchar();
        cmdline = realloc(cmdline, sizeof(cmdline) + sizeof(char));
        cmdline[counter] = c; 
        counter++;
    }
    return cmdline;
}

有没有办法解决这个问题,以便我可以用它阅读更大的行?

这里的问题是,

 sizeof(cmdline)

并不像您想象的那样工作。您无法在指针上使用 sizeof 获取分配内存的大小。您必须自己跟踪尺寸。

现在,补充一点错误,

realloc(): invalid next size: 0x00000000007ca010

基本上发生的是,

cmdline = realloc(cmdline, sizeof(cmdline) + sizeof(char));

实际上并不像您想象的那样调整分配的内存。因此,当 counter 值大到足以指向 out-of-bound 内存(在 cmdline[counter] 的情况下)时,您实际上调用了 undefined behavior。因此,由于 UB(以及随后的内存损坏),realloc() 发出错误并引发分段错误作为副作用。

此外,要添加

  • 第 1 点:

     pointer = realloc(pointer....)
    

    是一个非常糟糕的语法。如果 realloc() 失败,您最终也会丢失实际内存,因为它将被 NULL 替换。

  • 第 2 点:

    getchar() returns 和 int 并且所有 return 值可能不适合 char(例如 EOF) .将 c 的类型更改为 int.

  • 第 3 点:

    您可能希望在 return 之前将您的 字符串 空终止,以使其可用于字符串操作函数并完美适合 %s printf().

  • 第 4 点:

    在使用 returned 指针之前,您应该检查 malloc()realloc() 是否成功。

使用 sizeof(cmdline) 没有达到您的预期。当与指针一起使用时,它将 return 指针的大小,而不是分配的内存。这对于在编译时定义的数组是不同的。

您可以使用 counter 变量来跟踪已经分配了多少字节。

还有一种风格:sizeof(char) 始终为 1,因此没有必要。

在每个字符之后重新分配是不高效的。最好分配一个适当大小的缓冲区,当它已满时分配一个更大的缓冲区。

结合所有建议:

#define BYTES_TO_ADD 80     /* a rather arbitrary number */

char * readCMDLine ( void ) {
    char * cmdline = NULL;
    size_t cmdSize = 0;
    size_t cmdLen = 0;
    int    c;

    while ((c = getchar()) != EOF) {
        if (cmdLen == cmdSize) {
            char * tmp = realloc(cmdline, (cmdSize += BYTES_TO_ADD));  
            if (tmp == NULL) { free(cmdline); return NULL; } 
            cmdline = tmp;
        }
        cmdline[cmdLen++] = (c == '\n') ? '[=10=]' : c;
        if (c == '\n') return cmdline;
    }
    return NULL;   /* no newline */
}

您的代码存在多个问题:

  • 您测试 while (c != 10),但是 c 没有第一次初始化,并且您不允许文件在下一个 '\n' 之前结束。您还假设 ASCII,有些系统今天仍在使用 EBCDIC,其中 '\n' 不是 10
  • 您使用 sizeof(cmdline) 而不是 counter 重新分配。 sizeof(cmdline)是指针的大小,不是当前分配的数组大小。
  • 您将 getchar() 存储到 char 变量中,EOF 不是适合该变量的值,也不可能是介于 128UCHAR_MAX 之间的值如果 char 在您的平台上签名。为此使用 int
  • 您没有空终止分配的字符串。 mallocrealloc 都没有初始化分配的内存。
  • 您永远不会检查 mallocrealloc 失败。
  • 您可能应该 return NULL 在文件结尾处,否则无法从空行序列中判断文件结尾。
  • 你为每个字符重新分配缓冲区,这样效率低下,但可以在后期优化,先修复代码。

这是更正后的版本:

#include <stdlib.h>

char *readCMDLine(void) {
    char *cmdline = malloc(1);
    size_t counter = 0, size = 1;
    int c;

    if (cmdline == NULL)
        return NULL;

    while ((c = getchar()) != EOF && c != '\n') {
        char *p = realloc(cmdline, counter + 2);
        if (p == NULL) {
            free(cmdline);
            return NULL;
        }
        cmdline = p;
        cmdline[counter++] = c;
    }
    cmdline[counter] = '[=10=]';
    if (c == EOF && counter == 0) {
        free(cmdline);
        cmdline = NULL;
    }
    return cmdline;
}

这是一个优化后的版本,调用 realloc 的次数少得多:

#include <stdlib.h>

#define ALLOCATE_INCREMENT  100
char *readCMDLine(void) {
    char *p, *cmdline = NULL;
    size_t counter = 0, size = 0;
    int c;

    while ((c = getchar()) != EOF && c != '\n') {
        if (counter > size - 2) {
            p = realloc(cmdline, size += ALLOCATE_INCREMENT);
            if (p == NULL) {
                free(cmdline);
                return NULL;
            }
            cmdline = p;
        }
        cmdline[counter++] = c;
    }
    if (counter == 0) {
        if (c == EOF || (cmdline = malloc(1)) == NULL)
            return NULL;
    } else {
        p = realloc(cmdline, counter + 1);
        if (p != NULL)
            cmdline = p;
    }
    cmdline[counter] = '[=11=]';
    return cmdline;
}