Return 来自以指针为参数的函数的指针

Return Pointer from function with pointer as param

我正在读这本书:"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 解决方案有效?

如果我没理解错的话是不是

  1. 在堆栈上分配char buffer[10];
  2. buffer的第一个元素的地址分配给我的ptr(住在以前的范围内)ptr = buffer;

我的期望:

buffer 上的 ptr 点应该是假的,因为这个范围应该是 broken/cleaned 向上。

我的想法有什么问题吗?

编辑 1

我将 test4(ptr); 更改为 ptr = test4(ptr),它仍然有效...

仍然不知道为什么 test4(char* ptr) 正在工作...

char *test4(char *ptr) {
    char buffer[10];
    ptr = buffer;
    strcpy(buffer, "teswert");
    return ptr;
}

此代码除了 return 无效指针外没有做任何事情。您的理解是正确的,堆栈指针 returned 无效,不应读取。

这个"works"的原因是因为实际上没有使用那个指针。

test4(ptr);

指针的副本被传递,return值被丢弃,所以它什么都不做。印刷文字来自test3。举个例子,您可以更改 "testwert" 并且您得到的印刷品完全相同,如果您更改 test3 中的那个,它会更改两个印刷品。所以换句话说,这本书犯了一个错误并用另一个错误隐藏它,然后它没有注意到所有的错误,因为它测试代码有多糟糕(如果它不会 "testwert" 四次,错误会很明显,当然,任何称职的编译器都会发出警告。

我建议把那本书扔掉。


对于 ptr = test4(ptr) 的编辑版本,这是未定义的行为,所以任何事情都可能发生。这包括打印预期的输出、打印垃圾、使程序崩溃或更糟。

您的想法没有错 - 您完全正确。干得好,你现在的 C 编程语言水平比本书作者还高。

这本书一文不值 - 第 3 次修订版,它教授 3 年前的 C 语言的过时版本,其中包含非常糟糕的示例。你刚好 幸运 test4。将数组的第一个元素的地址放在 some 编译器中只会抑制警告,并且数组恰好位于堆栈中的正确位置并且没有被覆盖。 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 仍然 指向从 test3 返回的值,它是从堆中分配的。当您将其替换为 ptr = test4(ptr); 时,您将获得完全未定义的行为,因为 ptr 现在指向一个已过其生命周期的变量。当未定义的行为发生时,程序可能会做任何事情,包括 (C11 3.4.3p1):

[...] ignoring the situation completely with unpredictable results [...]

"unpredictable results" 包括它工作的可能性 "as intended"。


上一个公告点列出了其中一个选项

  • [Sie verwenden] einen beim Aufruf der Funktion als Argument übergebenen Puffer [...]

[您将使用]一个缓冲区作为参数传递给函数。对于此选项,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 chars 的数组。