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
不是适合该变量的值,也不可能是介于 128
和 UCHAR_MAX
之间的值如果 char
在您的平台上签名。为此使用 int
。
- 您没有空终止分配的字符串。
malloc
和 realloc
都没有初始化分配的内存。
- 您永远不会检查
malloc
或 realloc
失败。
- 您可能应该 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;
}
我正在开发一个函数,使用 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
不是适合该变量的值,也不可能是介于128
和UCHAR_MAX
之间的值如果char
在您的平台上签名。为此使用int
。 - 您没有空终止分配的字符串。
malloc
和realloc
都没有初始化分配的内存。 - 您永远不会检查
malloc
或realloc
失败。 - 您可能应该 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;
}