我遇到了一家公司的两个问题。这两个问题让我困惑。 任何人都可以帮助解释答案的原因吗?
写下结果。
void Test(void){
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL){
strcpy(str, “world”);
printf(str);
}
}
Ans:它将输出“world”
写下结果。
char *GetMemory(void){
char p[] = "hello world";
return p;
}
void Test(void){
char *str = NULL;
str = GetMemory();
printf(str);
}
Ans:输出未知,因为指针无效。
答案 0 :(得分:9)
两者都会产生undefined behaviour。
第一个是因为你在释放它之后使用指针(str
)(free()
在块之后没有/不能将指针设置为NULL。
第二个因为您正在使用指向另一个函数的局部变量的指针。
答案 1 :(得分:4)
两个示例都包含相同类型的错误,在生命周期结束后使用对象。
这样做会导致未定义的行为 1 。该文本使用“引用”一词,这基本上意味着访问一个对象或使用所述对象的标识符。
实际上在两个例子中导致未定义行为的原因并不是上面提到的规则 1 ,而是另一个与之密切相关的规则。指向生命周期结束的对象的指针值是不确定的 2 。读取此类值会导致未定义的行为。这两种情况都会发生这种情况。
在第一个例子中,str的生命周期在免费调用时结束。然后在if语句if(str != NULL){
中使用该指针,这会导致未定义的行为。
在第二个例子中,当函数返回时,p的生命周期结束。返回的指针被分配给指针str:str = GetMemory();
,这会导致未定义的行为。
1 (引自:ISO / IEC 9899:201x 6.2.4对象2的存储持续时间)
如果一个对象被引用到它之外
一生,行为未定义。
2 (引自:ISO / IEC 9899:201x 6.2.4对象2的存储持续时间)
当指针变为不确定时,指针的值变得不确定
它指向(或刚刚过去)的对象到达其生命周期的末尾。
答案 2 :(得分:1)
这两个例子都是"未定义行为的经典案例"。在第一种情况下,在释放它之后,您正在使用malloc的内存。在第二种情况下,您将返回一个指向其内存位于堆栈中的字符串的指针 - 一旦执行GetMemory
中的返回,该指针将不再有效。
然而,"未定义的行为"意味着编译器可以做任何喜欢的事情,两种情况下的实际情况是编译器很可能只是让你重用内存。
在第一种情况下,当你释放内存时,通常发生的是块被放在一个空闲列表中以便以后重用。您可以strcpy
找到它,但可能会损坏malloc
的免费列表。但除非malloc
尝试分配新内存,否则这无关紧要。此外,无论你strcpy
,除非malloc
将内存重新分配给其他地方,否则它将保持不变。这就是为什么对于某些实现(可能是大多数实现),第一个示例确实打印了#34; world"。但是,如果此函数是较大程序的一部分,则稍后可能会出现无法解释的崩溃。
在第二种情况下,来自字符串的内存是从堆栈中分配的。在这种情况下,printf
需要堆栈空间,因此很可能会覆盖包含字符串字符的内存。 printf
可能会打印一个垃圾字符串,但由于您将它作为第一个参数传递,它会尝试替换它在垃圾字符串中找到的任何格式说明符。所以你可能会得到垃圾,或者你可能会崩溃。