关于在函数中返回局部指针变量的问题

A question about returning local pointer variable in function

我知道函数中的变量正在使用堆栈space。当函数退出时,space 被释放。这就是为什么我们应该在函数中将指针变量声明为静态的。但是,我发现下面的代码运行良好。

gcc 版本为:gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)

#include <stdio.h>

char *month_name(int n) {
    char *name[] = {"Invalid name", "Jan.", "Feb", "Mar", "Apr", "May", "June",
                    "July",         "Aug",  "Sep", "Oct", "Nov", "Dec"};

    return n < 1 || n > 12 ? name[0] : name[n];
}

int main() {
    char *month;
    month = month_name(2);
    printf("%s\n", month); // The output is Feb
}

函数中的变量似乎隐式转换为静态。谁能为我解释一下?提前致谢。

您在这里 return 不是本地数据。本地数据是数组。它包含指向字符串文字的指针,这些文字存储在常量只读存储器中。它们的位置或寿命不会改变。所以可以 return 指向它们的指针。

但是,如果您尝试 return 指向数组的指针,那将是错误的。

您声明了一个指向字符串文字(指向它们的第一个字符)的指针数组

char *name[] = {"Invalid name", "Jan.", "Feb", "Mar", "Apr", "May", "June",
                "July",         "Aug",  "Sep", "Oct", "Nov", "Dec"};

字符串文字具有静态存储持续时间。即退出函数后还活着

例如在 C 标准(6.4.5 字符串文字)中写着

6 In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals.78) The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence....

另一方面,数组本身有自动保存期限,即退出函数后不存在。但是函数 return 是一个指向字符串文字的指针,而不是指向数组本身的指针。

如果您尝试 return 指向数组本身的指针,例如

,该函数将不正确
char * ( *month_name(int n) )[13] {
    char *name[] = {"Invalid name", "Jan.", "Feb", "Mar", "Apr", "May", "June",
                    "July",         "Aug",  "Sep", "Oct", "Nov", "Dec"};

    //...     
    return &name;
}

或以下方式

char ** month_name(int n) {
    char * name[] = {"Invalid name", "Jan.", "Feb", "Mar", "Apr", "May", "June",
                    "July",         "Aug",  "Sep", "Oct", "Nov", "Dec"};

    return n < 1 || n > 12 ? name : name + n;
}

或者,如果您声明一个二维数组,例如

char *month_name(int n) {
    char name[][13] = {"Invalid name", "Jan.", "Feb", "Mar", "Apr", "May", "June",
                    "July",         "Aug",  "Sep", "Oct", "Nov", "Dec"};

    return n < 1 || n > 12 ? name[0] : name[n];
}

然后在这种情况下 return 声明

return n < 1 || n > 12 ? name[0] : name[n];

确实会调用未定义的行为,原因与退出函数后数组本身将不存在的原因相同。

注意在C++中与C相反的字符串文字有常量字符数组类型。因此,要将您的函数编译为 C++ 代码,您必须按以下方式定义函数

const char *month_name(int n) {
    const char *name[] = {"Invalid name", "Jan.", "Feb", "Mar", "Apr", "May", "June",
                    "July",         "Aug",  "Sep", "Oct", "Nov", "Dec"};

    return n < 1 || n > 12 ? name[0] : name[n];
}

同样在 C 中,最好以相同的方式定义函数,因为尽管在 C 中字符串文字具有 non-constant 字符数组类型,但是任何更改字符串文字的尝试都会调用未定义的行为。这样的函数定义可以避免程序错误。