使用C字符串会出现警告:"返回与本地变量关联的堆栈内存的地址"。

61

我不是C语言程序员,所以对C字符串不是很熟悉,但是现在我必须使用一个C库,以下是我的代码的缩短版本,以展示我的问题:

char** ReadLineImpl::my_completion () {
    char* matches[1];
    matches[0] = "add";

    return matches;
}

我收到了这个警告:

警告 - 返回与本地变量“matches”关联的堆栈内存地址

我的程序似乎不能正常工作(可能是由于上述警告引起的)。

这个警告意味着什么?它会导致任何问题吗?


5
你正在返回指向分配在堆栈上并且在函数结束后就不存在的第一个字符指针的地址。 - Uchia Itachi
可能是重复问题:*C++ 返回对局部变量的引用* - Peter Mortensen
4个回答

80

char* matches[1] 这个变量是在栈上声明的,当当前块超出范围时,它将被自动释放。

这意味着当你返回 matches 时,为 matches 保留的内存将被释放,你的指针会指向你不想要的东西。

你可以用多种方式解决这个问题,其中一些方式如下:

  1. matches[1] 声明为 static: static char* matches[1]; - 这将在静态空间而不是栈上分配 matches 的空间(如果使用不当,可能会有问题,因为所有 my_completion 函数的实例都共享同一个 matches 变量)。

  2. 在调用函数中分配空间,并将其传递给 my_completion 函数:my_completion(matches)

char* matches[1];
matches = my_completion(matches);

// ...

char** ReadLineImpl::my_completion (char** matches) {
     matches[0] = "add";

     return matches;
}
  • 在被调用函数中使用堆上的内存分配函数(如malloccalloc等)为其分配空间,并将所有权传递给调用者函数。当不再需要时,调用者函数必须释放此空间(使用free函数)。


  • 1
    谢谢,我知道了。最糟糕的是,这已经是我第二次遇到这种问题了:) 无论如何,非常感谢。 - khajvah
    假设这是GNU readline,这将导致崩溃,因为readline将释放完成函数返回的内存。 - Mats Petersson
    @MatsPetersson 这似乎是readline - 如果是这样,你是正确的。我会点赞你的回答! - Nemanja Boric
    7
    matches声明为static会在静态空间上分配空间,而不是在堆上分配空间。 - MAnyKey
    2
    第三个选项是通过调用calloc在堆中分配内存。 - Mike Glukhov

    14

    当你返回matches数组时,你实际上是在返回第一个元素的地址。这个地址存储在my_completion内部的堆栈中。

    一旦你从my_completion返回,那块内存就被回收了,很可能最终会被重用于其他用途,覆盖matches中存储的值——是的,这很可能就是为什么你的应用程序不能正常工作的原因——如果现在它还没有出问题,那么在你修复其他问题、稍微修改一下代码或者做其他操作后,它很可能就会出问题,因为这不是那种可以安全忽略的小警告。

    你可以用几种不同的方法来解决这个问题。其中最明显的方法是简单地使用std::vector<char *>[更好的选择是std::vector<std::string>]:

    std::vector<std::string> ReadLineImpl::my_completion ()
    {
        std::vector<std::string> strings;
        strings.push_back("add");
        return strings;
    }
    

    因此,如果库要求使用readline接口的char **,那么请使用以下代码:

    char** ReadLineImpl::my_completion ()
    {
        char **matches = static_cast<char **>malloc(1 * sizeof(char *));
        matches[1] = "add";
        return matches;
    }
    

    问题已解决!


    作为C++程序员,我会使用向量(vector),但库要求使用char,所以我必须将其转换为char - khajvah
    所以,假设您正在使用readline接口,则必须使用malloc分配内存,因为readline稍后会释放它。我会进行编辑。 - Mats Petersson
    好的,只有一件事,我不得不使用(char**)malloc(1 * sizeof(char *)),但谢谢,你的答案更好,虽然我不会改变。 - khajvah

    2

    使用堆而不是栈

    对于这种情况,最好使用堆来分配内存:

    int* someDataForParams(void *_params) {
    
        // ...
    
        int* charCounts = (int*) calloc(96, sizeof(char*));
    
        // ...
    
        return charCounts;
    }
    

    96只是一个字符串长度(只是一个神奇数字)。


    1

    有两种解决方案:

    第一种是使用关键字static将变量声明为静态变量。

    第二种解决方案是使用malloccalloc进行动态分配。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接