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

C警告:函数返回局部变量的地址,c,pointers,malloc,C,Pointers,Malloc,下面的函数接受argv[0]参数,该参数包含应用程序的调用路径,并将最后一位替换为“/”,直到它使用位于同一文件夹中的我要生成的新应用程序的名称 顺便说一句:我声明了一个全局argv变量,以便函数可以访问它,因为我不想在每个函数调用中传递信息 void returnFullPath(char* fullPath, int maxLength) 当我编译代码时,一切似乎都正常,但我得到了上面的警告 我知道我正在声明变量,函数一返回它就会被销毁 作为一名初级C程序员,我想知道解决这个问题最优雅/最

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

顺便说一句:我声明了一个全局argv变量,以便函数可以访问它,因为我不想在每个函数调用中传递信息

void returnFullPath(char* fullPath, int maxLength)
当我编译代码时,一切似乎都正常,但我得到了上面的警告

我知道我正在声明变量,函数一返回它就会被销毁

作为一名初级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;
}
替换

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


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

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


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

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

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

static char bidbotPath[....];
但是!它不能与可变长度一起工作

2) 使用
malloc

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

在使用完它之后,你应该打电话给免费的(bidbotPath)

其他一些答案建议你购买一些东西并归还。这是一个坏的实践,在C++中,当你在一个函数中新建一个函数,而调用方应该删除它(谁拥有)? 许多C API采用以下格式是有原因的:

function(buf, length);
这意味着调用方提供缓冲区以及缓冲区的长度。调用者负责分配和取消分配这个缓冲区,您的函数应该使用它,并检查您不会溢出长度


不要在马洛克和返回。这只是自找麻烦。

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

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

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

不完全是您需要的,但是这里有一些我使用过的代码。我把它作为一个练习留给学生,让他们得到你需要的东西:

cp = strrchr( argv[0], '/' ); if ( cp ) cp++; else cp = argv[0]; cp=strrchr(argv[0],“/”); 如果(cp) cp++; 其他的
cp=argv[0] 如果可能的话,函数最好是指向可以写入返回值的内存的指针。我这么说是因为您允许您的客户机(函数的调用者)选择内存的位置:在堆栈或堆上,或者甚至在更奇特的地方


现在,这里的关键是“如果可能”条款。有时,内存的大小只能在函数执行期间确定。一个典型的例子是以null结尾的字符串的函数。当你遇到这种情况时,通常最好在函数堆内分配内存,并要求客户端在完成内存时释放内存。对新函数的任何子提示调用,都不可避免地会写入保存您打算返回的信息的内存。话虽如此,解决这个问题有两种方法

客户方所有权 正如Moo Juice所建议的那样,一种方法是向调用中添加一些参数,授权在函数调用后保持信息的责任

void returnFullPath(char* fullPath, int maxLength)
在完成之前,通过调用strncpy()将结果复制到输出参数

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

提供者方所有权 然而,对于这种语言,还有另一种被接受的方法。例如,它是stdio.h库使用的。如果要打开文件,请使用结构文件作为指针。在本例中,stdio为我们提供了fopen和fclose两个函数,一个用于分配资源,另一个用于取消分配资源。这利用了一个称为抽象数据类型的概念,它是我们在结构化编程中所见到的最接近对象的东西。有关ADT的更多详细信息,请参阅。在这种情况下,一个完整的ADT对于你正在做的事情来说似乎是一种荒谬的过度杀伤力,但它符合你的想法

在这种情况下,需要分配和取消分配两种功能

char* getFullPath(); /* here is where you do malloc*/
void disposeFullPath(char* fullPath); /* and here, free */
这样,您就可以精确地分配所需的内存量


关于你的问题,我想谈几点看法

  • 只要你有能力,尽量坚持ANSI标准。维基百科是不是
    strncpy(fullPath, bidbotPath, maxLength);
    
    char* getFullPath(); /* here is where you do malloc*/
    void disposeFullPath(char* fullPath); /* and here, free */