"address of stack memory associated with local variable" 在 C 中 运行 用户定义函数时出错

"address of stack memory associated with local variable" error when running user-defined function in C

最近我一直在学习哈佛大学的 CS50 2020 课程,作为 C 编程的介绍。我对这门语言或整个编码都不是很有经验,所以我很难弄清楚我的代码有什么问题。

我写了这个小函数,它应该接受一个字符串,然后通过调用另一个函数,使用凯撒密码加密文本,然后 return 它作为一个字符串。问题是,我不知道如何 return 字符数组作为字符串。在阅读了一些有关该问题的信息后,我尝试在数组末尾添加一个 NUL 字符,并且编译正常,但是当我 运行 程序时,我收到以下错误消息:

error: address of stack memory associated with local variable 'result' returned [-Werror,-Wreturn-stack-address]
    return result;
           ^~~~~~

我的代码:

string encypher(string text)
{
    int length = strlen(text);
    char result[length];
    for(int i = 0; i < length; i++)
    {
        int letter_c = test_char(text[i]);
        result[i] = (char)letter_c;
    }
    result[length + 1] = '[=13=]';
    return result;
}

这里的问题是,result 作为一个数组,在表达式中使用时衰减为指向其第一个元素的指针,这就是从函数返回的内容。并且因为数组的生命周期在函数 returns 时结束,该指针现在指向一个无效的内存位置,并且尝试使用它会调用 undefined behavior.

不创建本地数组,而是使用 malloc 函数动态分配内存。该内存在程序的生命周期内有效,或者直到返回的指针传递给 free:

string result = malloc(length + 1);

另请注意,您需要为用于终止字符串的空字节预留一个额外字节。

在行

return result;

数组decays指向一个指针,所以它实际上是:

return &result[0];

这个数组是在函数encypher中分配在栈上的,所以当函数returns时它就不再存在了。因此,返回的指针是 dangling pointer,这意味着它指向不再分配的内存,可能会被其他东西覆盖。因此,不应使用这样的指针。

为了分配在函数 returns 之后仍然存在的内存,您可以:

  • 使用动态内存分配,例如malloc
  • 在函数调用 encypher 的堆栈上分配内存(而不是在函数 encypher 本身)并更改函数 encypher 的参数以接受指向那个数组。

在我看来,第二种解决方案是更简洁的解决方案,因为它允许调用者决定在何处以及如何分配内存。使用该解决方案,您的代码将如下所示:

void encypher( char *cyphertext, const char *plaintext )
{
    int length = strlen(plaintext);
    //removed: char result[length];
    for(int i = 0; i < length; i++)
    {
        int letter_c = test_char(plaintext[i]);
        cyphertext[i] = (char)letter_c;
    }
    cyphertext[length + 1] = '[=10=]';
}

函数现在可以这样调用:

int main( void )
{
    char plaintext[23] = "This is the plaintext.";
    char cyphertext[23]; //make sure the buffer is large enough to store the cyphertext including the terminating null character

    encypher( cyphertext, plaintext );

    return 0;
}