您可能需要在编译器中打开更多警告或获得更好的编译器。当我编译你的代码,我得到了警告:
mem.c: In function ‘main’:
mem.c:12: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’
mem.c:12: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’
mem.c: At top level:
mem.c:7: warning: unused parameter ‘argc’
mem.c:7: warning: unused parameter ‘argv’
在MacOS
我不明白为什么重复上线12的警告,但两者GCC 4.2和4.6.1 X 10.7给它的两倍。当您不使用命令行参数时,关于argc
和argv
的警告是使用int main(void)
的好理由;他们不是一个大问题。
关于%s
和int
和char *
的警告(真的是错误)足以说明您的崩溃 - 但这不是代码的唯一问题。事实上,也有:
- 一个初期的内存泄漏,
- 实际泄漏,
- 你在哪里(意外)依赖未定义行为的地方,
- 一个地方,你”重新依赖实现定义的行为,这可能不符合您的期望。
您的代码注释:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int f1(char **str_);
int main(int argc, char **argv)
{
char *str = NULL;
f1(&str);
printf("str : %s\n", *str);
str = realloc(str, (size_t) 0); /* (3) Undefined behaviour */
assert(str == NULL); /* (4) Implementation-defined behaviour */
return 0;
}
int f1(char **str_)
{
if ((*str_ = realloc(*str_, sizeof(char) * 12)) == NULL) /* (1) Incipient leak */
{
fprintf(stderr,"realloc() failed\n");
exit(3);
}
(*str_) = "hello there"; /* (2) Actual leak */
return 0;
}
按编号顺序在讨论这些:
如果内存重新分配失败的初期泄漏发生。 *str
是存储指向已分配内存的指针的前一个值的唯一位置,如果realloc()
失败,则返回0(空指针),但不释放旧内存,但不再有指向旧内存的指针。
的修复:
char *new_mem = realloc(*str, 12);
if (new_mem == 0)
...error handling...
*str = new_mem;
拇指的规则:不要realloc()
的返回值分配到这是它的第一个参数变量。
由于您将指针指向指向新分配内存的指针的字符串常量,因此出现实际泄漏。最简单的解决方法是使用strcpy()
,但您需要在此时添加#include <string.h>
。你会还,通常情况下,要确保你分配你要复制字符串只是足够的空间,从而导致:
char const hello[] = "hello there";
char *new_mem = realloc(str, sizeof(hello));
//char *new_mem = realloc(str, strlen(hello)+1);
if (new_mem == 0)
...handle error...
*str = new_mem;
strcpy(new_mem, hello);
在这个例子中,我可以使用sizeof(hello)
因为字符串的长度包括空终止,并且因为实际的数组定义在范围之内。如果要复制的字符串作为指针传递到函数中,则使用strlen(hello)+1
的替代方法是正确的(并且使用sizeof()
不正确),即使它需要运行时长度计算而不是编译时间计算(如图所示)。
未定义的行为是由于(2)处的内存泄漏引起的。您尝试realloc()
一个字符串常量,而不是由realloc()
返回的指针。这导致了未定义的行为;它可能会崩溃(因为realloc()
试图将控制信息写入只读存储器),或者它可能会简化内存系统,导致一段时间后崩溃。
对此的修正是对项目(2)的修正。
实现定义的行为的出现是因为C标准说:
§7.20.3内存管理功能¶1:[...]如果空间的请求的大小为零,行为是实现定义的: 返回空指针,或者行为就好像大小是非零值,但返回的指针不能用于访问对象。
您断言您的实现将选择第一个选项,但它可能会选择第二个选项。解决办法是删除无根据的断言。
所以,这是共有5个问题的代码,其中只有一个编译器可能会帮助你。
很好,详细的解释各种问题。 –
非常好,先生。非常感谢你,我学到了很多东西。 – pharaoh