我正在读这本书:"C von A bis Z"。
有这个例子。
/* ptr14.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Fehler: Funktion gibt die Adresse
* einer lokalen Variablen zurück. */
/* [ Error: Function returns the address of a
a local variable. ] */
// ...
/* Möglichkeit2: Speicher vom Heap verwenden */
/* [ Option2: Use memory from the heap ] */
char *test3(void){
char *buffer = (char *) malloc(10);
strcpy(buffer, "testwert");
return buffer;
}
/* Möglichkeit3: Einen Zeiger als Argument übergeben */
/* [ Option3: Pass a pointer as argument ] */
char *test4(char *ptr){
char buffer[10];
ptr = buffer;
strcpy(buffer, "testwert");
return ptr;
}
int main(void) {
char *ptr;
/* ... */
ptr = test3();
printf("test3: %s\n", ptr);
test4(ptr);
printf("test4: %s\n", ptr);
return EXIT_SUCCESS;
}
我了解作者正在谈论的问题。
test4
解决方案为什么起作用?
如果我理解正确,不是吗
char buffer[10];
buffer
的第一个元素的地址分配给我的ptr
(在先前的范围内)ptr = buffer;
我的期望:
在ptr
上指向buffer
的点应该是错误的,因为此作用域应该被破坏/清理。
我的想法出了什么问题?
修改1
我将test4(ptr);
更改为ptr = test4(ptr)
,它仍然有效...
仍然不知道为什么test4(char* ptr)
工作正常...
答案 0 :(得分:7)
您的想法没有错-您是绝对正确的。做得好,与书的作者相比,您现在在C编程语言方面的资格更高。
这本书是一文不值的-第三修订版,其中讲授了3年前的陈旧版本的C,并提供了可怕的示例。您刚好幸运test4
。将数组的第一个元素的地址放入只是抑制了一些编译器中的警告,并且数组恰好位于堆栈上的正确位置且未被覆盖。 But GCC 8.3 isn't fooled by using an intermediate variable.
在功能中
char *test4(char *ptr){
char buffer[10];
ptr = buffer;
strcpy(buffer, "testwert");
return ptr;
}
在函数内使用ptr
不会影响函数外的指针。它在原始示例中有效,因为ptr
仍然是 still ,它指向从堆中分配的test3
返回的值。当您将其替换为ptr = test4(ptr);
时,您将获得完全未定义的行为,因为ptr
现在指向变量的生存期。而且,当发生未定义的行为时,程序可能会执行任何操作,包括(C11 3.4.3p1):
[...]完全忽略了情况,结果无法预测[...]
具有“无法预测的结果”,包括其“按预期”运行的可能性。
上一个公告点将以下选项之一列为
- [Sie verwenden] einen beim Aufruf der Funktion als ArgumentübergebenenPuffer [...]
即 [您将使用]作为参数传递给函数的缓冲区。对于此选项,test4
应该读为
// use the **array** starting from *ptr
char *test4(char *ptr){
// use a **different** string here so that you can verify
// that it actually *works* (max 9 characters!)
strcpy(ptr, "testval 4");
return ptr;
}
甚至也许
void test4(char *ptr){
strcpy(ptr, "testval 4");
}
文档说明,在调用此功能ptr
之前,应指向至少10个char
s的数组。
答案 1 :(得分:4)
char *test4(char *ptr) {
char buffer[10];
ptr = buffer;
strcpy(buffer, "teswert");
return ptr;
}
此代码除了返回无效的指针外,不执行其他任何操作。您的理解是正确的,返回的堆栈指针无效,不应读取。
之所以能够“成功”,是因为实际上并未使用该指针。
test4(ptr);
传递了指针的副本,并且丢弃了返回值,因此它什么也不做。打印的文本来自test3
。举例来说,您可以更改一个"testwert"
,并且获得的打印完全相同,如果您更改test3
中的打印,则会更改两个打印。因此,换句话说,这本书犯了一个错误,然后又将其隐藏起来,然后又没有注意到所有错误,因为它对代码的测试非常糟糕(如果不是四次"testwert"
,该错误将是显而易见的,当然,任何值得赞叹的编译器都将发出警告。)
我建议把那本书丢掉。
使用ptr = test4(ptr)
的编辑版本,这是未定义的行为,因此可能会发生任何事情。这包括打印预期的输出,打印垃圾,使程序崩溃甚至更糟。