我试图理解指针的指针是如何工作的,我得出了这个例子,它编译得很好.但是,当它执行时,我得到一个分段错误.PS:我不想f1()回来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);
assert(str == NULL);
return 0;
}
int f1(char **str_)
{
if ((*str_ = realloc(*str_, sizeof(char) * 12)) == NULL)
{
fprintf(stderr,"realloc() failed\n");
exit(3);
}
(*str_) = "hello there";
return 0;
}
Run Code Online (Sandbox Code Playgroud)
您需要在编译器中打开更多警告或获得更好的编译器.当我编译你的代码时,我收到警告:
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’
Run Code Online (Sandbox Code Playgroud)
我不太清楚为什么第12行的警告会重复,但是MacOS X 10.7上的GCC 4.2和4.6.1都给了它两次.当您不使用命令行参数时,警告argc和使用argv是一个很好的理由int main(void); 他们不是一个大问题.
有关警告(实际上,错误)%s和intVS 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;
}
Run Code Online (Sandbox Code Playgroud)
按编号顺序讨论这些:
如果内存重新分配失败,则会出现初期泄漏. *str是唯一存储指向已分配内存的指针值的地方,如果realloc()失败,则返回0(空指针),但不释放旧内存,但不再有指向旧内存的指针.
修复:
char *new_mem = realloc(*str, 12);
if (new_mem == 0)
...error handling...
*str = new_mem;
Run Code Online (Sandbox Code Playgroud)
经验法则:不要将返回值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);
Run Code Online (Sandbox Code Playgroud)
在这个例子中,我可以使用,sizeof(hello)因为字符串的大小包括null终止符,并且因为实际的数组定义在范围内.如果要复制的字符串作为指针传递给函数,那么替代使用strlen(hello)+1是正确的(并且使用sizeof()不正确),即使它需要运行时计算长度而不是如图所示的编译时计算.
由于(2)处的内存泄漏,出现了未定义的行为.你尝试realloc()一个字符串常量,而不是返回的指针realloc().这会导致不确定的行为; 它可能崩溃(因为realloc()尝试将控制信息写入只读内存)或者它可能只是搞乱了内存系统,导致一段时间后崩溃.
对此的修正是对项目(2)的更正.
出现了实现定义的行为,因为C标准说:
§7.20.3内存管理函数1:[...]如果请求的空间大小为零,则行为是实现定义的:返回空指针,或者行为就像大小是非零值一样,但返回的指针不得用于访问对象.
您断言您的实现将选择第一个选项,但它可能会选择第二个选项.修复是删除无根据的断言.
因此,代码中总共有5个问题,其中只有一个编译器可能会帮助您.
第一行将main()变量设置str为NULL并将指针传递给它f1.
f1跑得很好.结果f1是str内部的变量main现在是一个指向内存空间的指针,该空间包含字符串(文字)"hello there".
你的下一行printf,segfaults.为什么?因为您正在尝试打印*str(请注意这里的星号!!)作为字符串(格式说明符%s).什么*str时候被称为字符串?任何"地址"都可能被"地狱"表示(至少在32位机器上).令人怀疑的是,地址在您的流程空间中.
经典的段错误.
尝试传球str,而不是*str到printf.
这将有效,请参阅http://codepad.org/Mh00txen
代码还有其他问题,例如f1中12个字符的realloc除了导致内存泄漏之外什么都不做,因为你立即重新指定该指针指向字符串文字,但这不是你的段错误的原因.