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

时间:2011-08-01 11:42:36

标签: c pointers malloc

下面的函数采用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;
}

8 个答案:

答案 0 :(得分:45)

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

有许多C API的格式为:

function(buf, length);

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

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

答案 1 :(得分:4)

替换

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

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

这样你的变量就分配在堆上而不是堆栈上,所以在函数返回后它不会被删除。

答案 2 :(得分:3)

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

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

答案 3 :(得分:3)

首先要说的是,您收到的警告可能会被视为错误。任何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的更多详细信息,请参阅this。在这种情况下,对于你正在做的事情来说,一个完整的ADT似乎是荒谬的过度杀戮,但这与这个想法一致。

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

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

这样,你可以将你需要的确切内存量用于malloc


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

  • 只要您能够,请尽量遵守ANSI标准。 This是维基百科,但似乎准确无误。
  • 现在您正在使用C,您应该检查该语言的样式约定。检查this
  • 使用strrchar查找路径中的最后一个'/':here you go
  • 最后但同样重要的是:避免使用静态全局变量,它们只是令人头疼的事情

答案 4 :(得分:1)

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

有两种解决方法。

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

static char bidbotPath[....];

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

2)使用malloc

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

并且您应该在使用它之后致电free(bidbotPath)

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

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

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

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

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

  cp = strrchr( argv[0], '/' );
  if ( cp )
     cp++;
  else
    cp = argv[0];

答案 7 :(得分:-1)

当你试图调用任何函数然后自动在堆栈上分配内存时,但通常在执行函数定义之后,堆栈内存特别是如果你希望你的函数返回地址那么从堆栈内存中丢弃然后生成变量在函数定义中用作静态