关于以下代码的两个问题:
有和没有malloc()
声明之间有什么区别:
p = (char *) malloc (20 * sizeof(char) );
这只是内存位置的差异吗?指针变量' p
'在没有该声明的STACK中,在HEAP中是否有该声明?
为什么声明是
printf("%s\n", p);
不是
printf("%s\n", *p);
#include <stdlib.h>
#include <stdio.h>
int main () {
char movie[] = "forrest gump";
char *p;
p = (char *) malloc (20 * sizeof(char) );
p = movie;
printf("%p\n", p);
printf("%p\n", movie);
printf("%s\n", p); // to print "forrest gump"
free(p);
return 0;
}
答案 0 :(得分:5)
添加了行号注释的代码:
char movie[] = "forrest gump"; // 1
char *p; // 2
p = (char *) malloc (20 * sizeof(char) ); // 3
p = movie; // 4
printf("%p\n", p); // 5
printf("%p\n", movie); // 6
printf("%s\n", p); // to print "forrest gump" // 7
free(p); // 8
我将忽略malloc()
的返回值未被检查,并且只要您的编译器告知您malloc()
的返回值,我就不会对其进行分阶段处理你省略了malloc()
的声明 - 但是有很多人不同意这一点。 * sizeof(char)
不是必需的,可能更好地写为* sizeof(*p)
。
代码存在内存泄漏,并且由于内存泄漏而显示未定义的行为。
第1行很好。第2行同样很好,但可以通过将变量与第3行组合来初始化变量。
第3行在这种情况下是安全的,但是在其他情况下,分配的固定大小将是一个问题。
第4行是一个主要问题。这是泄漏。您只是践踏了malloc()
返回的内存的唯一指针,因此内存不可避免地丢失了。当然,这个程序将很快退出(如果它不首先崩溃),但总的来说,这很糟糕。你应该写:
strcpy(p, movie);
或者也许:
memmove(p, movie, sizeof(movie));
第5行将打印存储在p
中的地址。如上所述,由于第4行中的赋值,它是movie
的地址。可以说,它应该使用强制转换编写,因为%p
格式说明符需要void *
:
printf("%p\n", (void *)p);
在实践中,当p
为char *
时,您就会随处可见。我学会了在机器上编程(在有C标准的前几天),其中char *
地址的位表示与同一存储位置的anything_bigger *
地址不同。在这样的计算机上,如果代码处理struct Something *
甚至是int *
,则需要转换为void *
。
第6行产生与第5行相同的输出,并且具有相同的警告。
第7行很好,打印出p
指向的数据,这也是movie
指向的数据。
第8行是调用未定义行为的地方。由于第4行的分配,你试图释放未分配的空间,这是一场严重的灾难。它经常会导致崩溃;这绝不是一个好主意。
代码应合理地阅读:
char movie[] = "forrest gump";
char *p = malloc(sizeof(movie)); // or: char *p = malloc(strlen(movie) + 1);
if (p != 0)
{
strcpy(p, movie);
printf("%p\n", p);
printf("%p\n", movie);
printf("%s\n", p);
free(p);
}
你的问题2是关于:
printf("%s\n", p);
printf("%s\n", *p);
第一个是正确的。 p
是char *
,%s
转换规范需要char *
。第二个不正确,因为*p
是char
,但%s
需要char *
。相反,它会获得一个小整数(如果您的代码集基于ASCII,则为102)并尝试将其视为地址。这不起作用;它通常会导致崩溃,因为整个第一页内存(1 KiB或4 KiB)通常被映射为无效。
您可以使用:
printf("%c\n", *p);
会打印f
。
答案 1 :(得分:0)
大多数情况下,是的。 malloc
分配堆空间并返回指向它的指针。当函数进入时,为任何局部变量分配堆栈空间。在这种情况下,对于数组movie
,这是13个字节(“forrest gump”具有空终止符,可能更多取决于对齐问题和字符宽度)。将使用指定的数据初始化movie
。如果字符串更长,它将从编译器设置的常量数据中的副本中复制(直到过去十年左右,它会用任何字符串完成,但是现在似乎至少有一些编译器初始化短字符串直接与机器代码)。在堆栈上分配了指针p
的空间。
printf
将%s
的行为定义为期望指针。在堆栈上放置一个“字符串”(而不是指向它的指针)会使语言的实现变得非常复杂,因为它们的大小可变。字符串不是C中的特殊构造,因为它们在许多其他语言中。在C中,它们只是一个由null
或零字节终止的字符数组(只有字符串库函数关心它 - 编译器没有,除了可能用于错误检查)。