C警告:函数返回局部变量的地址

下面的函数采用argv [0]参数,该参数包含应用程序的调用路径,并替换最后一位,直到它遇到一个“/”,其中包含我要生成的新应用程序的名称,该应用程序位于同一文件夹中。

BTW:我正在声明一个全局argv变量,因此该函数可以访问它,因为我不想在每个函数调用中传递信息。

当我编译我的代码时,一切似乎都有效,但我得到了上述警告。

我知道我正在声明变量,并且只要函数返回它就会被销毁。

作为初学C程序员,我想知道解决这个问题的最优雅/最简单的方法是什么?

我应该将指针传递给函数或malloc一些内存吗?

char *returnFullPath() { char pathToApp[strlen(argv[0])+1]; strcpy(pathToApp, argv[0]); int path_length = strlen(argv[0]); while (pathToApp[path_length] != '/') { path_length--; } if (path_length > 2) pathToApp[path_length+1] = '\0'; else pathToApp[0] = '\0'; // length of getcwd + length of pathtoapp + 1 for zero plus 6 for "bidbot" char bidbotPath[strlen(getcwd(NULL,0)) + strlen(pathToApp) + 1 + 6]; sprintf(bidbotPath, "%s/%sbidbot", getcwd(NULL,0), pathToApp); return bidbotPath; } 

其他一些答案表明你将某些内容复制并返回它。 这与C ++中的错误做法一样,当你在函数中新建一个东西并且调用者应该删除它时(谁拥有所有权?)

有许多C API的格式为:

 function(buf, length); 

这意味着CALLER提供缓冲区以及它的持续时间。 IT是调用者负责分配和取消分配此缓冲区,并且您的函数应该使用它,并检查您是否不会超出该长度。

不要malloc并返回。 这只是在寻找麻烦。

更换

 char bidbotPath[strlen(getcwd(NULL,0)) + strlen(pathToApp) + 1 + 6]; 

 char* bidbotPath = malloc(strlen(getcwd(NULL,0)) + strlen(pathToApp) + 1 + 6); 

这样,您的变量在堆上而不是在堆栈上分配,因此在函数返回后不会删除它。

如果可能的话,函数指向可以写入返回值的内存的指针总是更好。 我这样说是因为你允许你的客户端(函数的调用者)选择在哪里找到内存:在堆栈或堆上,或者甚至是某些更具异国情调的地方。

现在,这里的踢球者是if可能的条款。 有时,只能在函数实现期间确定内存的大小。 一个典型的例子是一个以null结尾的字符串的函数。 当您遇到这种情况时,通常最好使用在函数内部的堆上分配内存,并要求客户端在完成内存时释放内存。

首先要说的是,你得到的警告可能会被视为错误。 任何subsecuent调用一个新函数,都将不可避免地写入持有你想要返回的信息的内存。 话虽如此,有几种方法可以解决这个问题。

客户双方所有权

正如Moo-Juice建议的那样,可以在调用中添加一些参数,委托在函数调用之后使信息持久化的责任。

 void returnFullPath(char* fullPath, int maxLength) 

在完成之前,通过调用strncpy( http://www.cplusplus.com/reference/cstring/strncpy/ )将结果复制到输出参数。

 strncpy(fullPath, bidbotPath, maxLength); 

这样,您可以确保函数调用者是内存的所有者,分配和取消分配它。 而且你不会尝试使用未分配的内存。

供应商双方所有权

但是,还有另一种方法,也适用于这种语言。 例如,它是由stdio.h库使用的。 如果要打开文件,请使用结构FILE作为指针。 在这种情况下,stdio为我们提供了fopen和fclose两个函数,一个分配资源,另一个分配它们。 这使用了一个名为Abstract Data Type的概念,它与我们在结构化编程中看到的对象最接近。 有关ADT的更多详细信息,请参阅此处。 在这种情况下,对于你正在做的事情来说,一个完整的ADT似乎是荒谬的过度杀戮,但这与这个想法一致。

对于这种情况,需要function,分配和解除分配。

 char* getFullPath(); /* here is where you do malloc*/ void disposeFullPath(char* fullPath); /* and here, free */ 

这样,您可以malloc所需的确切内存量


与您的问题相关,我想发表一些意见。

  • 只要您能够,请尽量遵守ANSI标准。 这是维基百科,但似乎准确。
  • 现在您正在使用C,您应该检查该语言的样式约定。 检查一下 。
  • 使用strrchar找到路径中的最后一个’/’: 这里你去
  • 最后但同样重要的是:避免使用静态全局变量,它们只不过是头疼

当函数返回时,将释放(解除分配)局部变量,并将内存用于其他内容。 如果返回局部变量的地址,它可能(并且应该)导致问题。

有两种方法可以解决这个问题。

1)使用static变量。 函数退出时不释放静态局部变量。

 static char bidbotPath[....]; 

但! 它不适用于可变长度。

2)使用malloc

 char *bidbotPath = malloc(strlen(getcwd(NULL,0)) + strlen(pathToApp) + 1 + 6); 

并且你应该在所有使用它之后调用free(bidbotPath)

您必须使用malloccalloc动态分配bidbotPath变量内存。 然后,确保调用您的函数的代码实际上释放了您返回的malloc内存。 这是一种常见的做法,也是C函数的常用习惯用法,它返回指向“生成”数组的指针。

 char * bidbotPath = (char*)malloc(strlen(getcwd(NULL,0)) + strlen(pathToApp) + 1 + 6); 

由于BidotPath在函数体的范围内被声明为普通的堆栈变量,因此当函数返回时它将消失。 虽然你的程序现在可以运行,但它只是运气好,如果其他代码在你的调用者之前重用旧的堆栈区域,它可能会在以后失败。

你可以声明bobotPath static,它会保留它,但是会阻止函数保持线程安全。 你可以做一个适当长度的malloc并返回它以保持函数线程安全,但是调用者需要释放内存以避免泄漏。 最好提供一个char数组和length,将数据放入函数的参数中。 在这里思考snprintf() 。 在内部使用strncpy()和类似的例程来复制到目标,但要注意strncat()对你来说可能不太安全。

此外,您的代码需要应对argv [0]中可能没有斜杠的事实……只是可执行文件的名称。

不是你需要的,但这里有一些我用过的代码。 我把它作为练习留给学生来获得你需要的东西:

   cp = strrchr(argv [0],'/');
  如果(cp)
      CP ++;
  其他
     cp = argv [0]; 

当你试图调用任何函数然后自动内存被分配在堆栈上,但通常在执行函数定义后,堆栈帧被特别地从堆栈内存中丢弃,如果你想让你的函数返回地址然后创建函数中使用的变量定义为静态