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

wal*_*tty 12 c return-value literals string-literals storage-duration

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

海湾合作委员会版本是: 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
}
Run Code Online (Sandbox Code Playgroud)

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

Sam*_*nen 19

You\xe2\x80\x99 此处未返回本地数据。本地数据是数组。它包含指向字符串文字的指针,这些指针存储在常量只读存储器中。它们的位置或生命周期不会改变。所以返回一个指向它们的指针就可以了。

\n

但是,如果您尝试返回指向数组的指针,那就是错误的。

\n

  • 可能与参考 C++ 标准 §2.14.5.8 相关“普通字符串文字和 UTF-8 字符串文字也称为窄字符串文字。窄字符串文字的类型为“n const char 的数组”,其中 n 是大小如下定义的字符串,并且具有静态存储持续时间”和§3.7.1.1“所有没有动态存储持续时间、没有线程存储持续时间、并且不是本地的变量都具有静态存储持续时间。这些实体的存储应在计划期间持续” https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf (6认同)
  • @TommyAndersen 谢谢,这是一个非常宝贵的参考! (2认同)

Vla*_*cow 14

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

char *name[] = {"Invalid name", "Jan.", "Feb", "Mar", "Apr", "May", "June",
                "July",         "Aug",  "Sep", "Oct", "Nov", "Dec"};
Run Code Online (Sandbox Code Playgroud)

字符串文字具有静态存储期限。也就是说,它们在退出函数后还活着。

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

6 在转换阶段 7 中,将值为零的字节或代码附加到由一个或多个字符串文字产生的每个多字节字符序列。78)然后使用多字节字符序列来初始化静态存储持续时间和长度刚好足够的数组包含序列....

另一方面,数组本身具有自动存储期限,即退出函数后它不再存活。但该函数返回一个指向字符串文字的指针,而不是指向数组本身的指针。

例如,如果您尝试返回指向数组本身的指针,则该函数将不正确

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

    //...     
    return &name;
}
Run Code Online (Sandbox Code Playgroud)

或以下方式

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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];
}
Run Code Online (Sandbox Code Playgroud)

那么在这种情况下返回语句

return n < 1 || n > 12 ? name[0] : name[n];
Run Code Online (Sandbox Code Playgroud)

确实会出于同样的原因调用未定义的行为,即退出函数后数组本身将不再活动。

请注意,在 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];
}
Run Code Online (Sandbox Code Playgroud)

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