'A question about returning local pointer variable in function

I know the variables in function are using stack space. When function exit, the space are freed. That's why we should declare the pointer variable as static in function. However, I found that the code below works well.

The gcc version is: 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
}

It seems the variable in function is translate to static implicitly. Can anyone explain for me? Thanks in advance.



Solution 1:[1]

You declared an array of pointers to string literals (to their first characters)

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

String literals have static storage duration. That is they are alive after exiting the function.

For example in the C Standard (6.4.5 String literals) there is written

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....

On the other hand, the array itself has automatic storage duration that is it is not alive after exiting the function. But the function returns a pointer to a string literal instead of a pointer to the array itself.

The function would be incorrect if you tried to return a pointer to the array itself as for example

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

    //...     
    return &name;
}

or the following way

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;
}

Or if you would declare a two dimensional array like

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];
}

then in this case the return statement

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

indeed would invoke undefined behavior by the same reason that the array itself will not be alive after exiting the function.

Pay attention to that in C++ opposite to C string literals have types of constant character arrays. So to compile your function as a C++ code you have to define the function the following way

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];
}

Also in C it is much better to define the function the same way because though in C string literals have types of non-constant character arrays nevertheless any attempt to change a string literal invokes undefined behavior. Such a function definition allows to avoid program bugs.

Solution 2:[2]

You’re not returning local data here. The local data is the array. It contains pointers to string literals, which are stored in constant read only memory. Their location or lifetime does not change. So it is ok to return a pointer to them.

If you tried to return a pointer to the array, however, that would be wrong.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Sami Kuhmonen