从指针返回为参数的函数返回指针

时间:2019-04-01 07:29:26

标签: c

我正在读这本书:"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;

我的期望:

ptr上指向buffer的点应该是错误的,因为此作用域应该被破坏/清理。

我的想法出了什么问题?

修改1

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

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

2 个答案:

答案 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)的编辑版本,这是未定义的行为,因此可能会发生任何事情。这包括打印预期的输出,打印垃圾,使程序崩溃甚至更糟。