为什么以下代码有效:
char *p;
p="hello";
printf("%s\n",p);
虽然这个没有:
char *p;
strcpy(p,"hello");
printf("%s\n",p);
我知道添加p = malloc(4);在第二个例子将使代码工作,但这正是我的问题。为什么在第二个例子中需要malloc而在第一个例子中不需要malloc?
我在SO上寻找类似的问题,但没有人回答这个问题。
答案 0 :(得分:5)
p
是一个指针。你需要指出某事。在第一种情况下,
p = "hello";
使p
指向在运行时位于程序内存中某处的字符串文字。
在你的第二个案例中,你没有指出p
指向任何东西,所以做任何查看p
所指向的位置无效的内容。
p = malloc(some_size);
使p
指向一个可以容纳some_size
个字符的(未初始化的)内存。如果你保留足够的话,你可以做strcpy(p, "hello")
之类的事情,因为p
确实指向一个有效的内存区域,所以复制到p
指向的内存是可以的。请注意,some_size
必须至少与要复制到其中的内容一样大,包括'\0'
字符串终止符。
请注意:
p = "hello";
strcpy(p, "bye");
将无效,因为"hello"
可以存储为只读内存,因此您无法覆盖它。
答案 1 :(得分:0)
"hello"
是一个字符串文字,将存在于输出程序的一部分中。内存由编译器在编译时分配,与实际代码相同的位置 *
也就是说"hello"
的类型为const char[6]
,会自动转换为char *
。 (对于字符串文字,使用const char*
而不是char*
是一个很好的习惯。)
在第二种情况下,当您致电p
时strcpy
未经宣传,因此结果未定义。 p=malloc(4);
不足以解决这个问题,字符串“hello”由 6 个字符组成 - 来自单词hello本身加上{{1}终止字符串。
* 实际上在现代系统上并不完全正确,它在附近与代码相同。
答案 2 :(得分:0)
p="hello";
将字符串文字“hello”的地址分配给p
,而scanf
需要放置扫描输入的位置,因此需要分配一些内存(静态,动态或自动)。
答案 3 :(得分:0)
strcpy(p,"hello");
对于字符串文字hello
要复制,p必须指向有效的内存位置,在这种情况下不是。
答案 4 :(得分:0)
因为p
具有的值将是随机的,并且任何复制字符串的尝试都将导致崩溃。通过使用malloc,您可以确保p
的值可以复制(只要缓冲区足够大)
答案 5 :(得分:0)
我发现在这种情况下,图片会派上用场。
让我们将上面的两个片段结合起来:
char *p = "Hello";
char *q;
strcpy(q, "goodbye");
printf("p = %s, q = %s\n", p, q);
这是一张假设的记忆地图,显示p
,q
与字符串"Hello"
和"goodbye"
之间的关系:
Item Address 0x00 0x01 0x02 0x03 ---- ------- ---- ---- ---- ---- "Hello" 0x00080000 'H' 'e' 'l' 'l' 0x00080004 'o' 0x00 0x?? 0x?? "goodbye" 0x00080008 'g' 'o' 'o' 'd' 0x0008000C 'b' 'y' 'e' 0x00 ... p 0x01010000 0x00 0x08 0x00 0x00 q 0x01010004 0x?? 0x?? 0x?? 0x??
"Hello"
和"goodbye"
是字符串文字,它们是char
(C ++中的const char
)的数组,以这样的方式存储,即它们在生命周期内是活动的该程序。文字"Hello"
从地址0x00080000开始存储,文字“再见”从地址0x00080008开始存储。
p
和q
是指向char
范围auto
的指针,这意味着它们仅在声明它们的块的生命周期内存在。在这种情况下,它们分别位于地址0x01010000和0x01010004(在此示例中我们假设为32位指针)。
当您编写char *p = "Hello";
时,数组表达式"Hello"
将转换为指针表达式,其值是数组的第一个元素的位置,并且该指针值将复制到{{1} },如上面的记忆图所示。
当您编写p
时,char *q;
的初始值是不确定的 1 ,如q
字节值所示。该值可能对应于可写地址,也可能不对应;可能性不大。所以基本上,当你写0x??
时,你试图将strcpy(q, "goodbye");
字符串文字的内容复制到内存中的随机位置。通常情况下,这会导致运行时错误。
如果为字符串分配缓冲区,则缓冲区必须足够长以存储整个字符串和0终止符;仅分配4个字节是不够的,因为那时你的字符串会溢出到你没有“拥有”的内存中,可能会破坏一些重要的东西(技术上,行为是未定义的,意味着任何事情都可能发生)。 IOW,你不必为指针分配内存(当你声明指针时已经完成了),你必须为被指向的东西分配内存。
如果我们将代码段更改为
"goodbye"
然后我们的记忆图将如下所示:
Item Address 0x00 0x01 0x02 0x03 ---- ------- ---- ---- ---- ---- "Hello" 0x00080000 'H' 'e' 'l' 'l' 0x00080004 'o' 0x00 0x?? 0x?? "goodbye" 0x00080008 'g' 'o' 'o' 'd' 0x0008000C 'b' 'y' 'e' 0x00 ... p 0x01010000 0x00 0x08 0x00 0x00 q 0x01010004 0x40 0x00 0x00 0x00 ... <dynamic> 0x40000000 'g' 'o' 'o' 'd' 0x40000004 'b' 'y' 'e' 0x00 0x40000008 0x?? 0x??
在这种情况下,char *p = "Hello";
char *q;
q = malloc(10);
strcpy(q, "goodbye");
printf("p = %s, q = %s\n", p, q);
从0x40000000开始留出10个字节的内存,并将该地址复制到malloc
。然后,对q
的调用将字符串文字“goodbye”的内容复制到该位置。
<小时/> 1 如果
strcpy
被声明为q
或文件范围(任何函数之外),那么它将被初始化为0(0x00000000)。