12

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.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
waltermitty
  • 453
  • 3
  • 12
  • Doesn't `char* name[]` will be an array of string literals, making this (only a bit ill formed)/valid? [Assemby](https://godbolt.org/z/x8hx1r4dv) generated seems to suggest this. But in that case it should be `const char*` – Lala5th May 05 '22 at 09:49
  • You're not returning a pointer to an array element but the value of an array element (which happens to be a pointer, but that's not important). This is pretty much the same situation as `int f() { int v[] = {0}; return v[0]; }`. which you know is not a problem. – molbdnilo May 05 '22 at 10:44
  • 2
    FYI, since the array is never changed and you do not need multiple copies of it, you ought to make the array static and `const` as well: `static char * const name[] = { … };`. The pointed-to data could also be `const`: `static const char * const name[] = { … };`, if you change the function return type and the calling code to match. – Eric Postpischil May 05 '22 at 11:44
  • 1
    Do not tag both C and C++ except when asking about differences or interactions between the two languages. Based on the use of `char *` to refer to string literals rather than the `const char *` that is needed in C++, I have removed the C++ tag. – Eric Postpischil May 05 '22 at 11:45

2 Answers2

19

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.

Sami Kuhmonen
  • 30,146
  • 9
  • 61
  • 74
  • 6
    Might be relevant to reference the C++ standard §2.14.5.8 "Ordinary string literals and UTF-8 string literals are also referred to as narrow string literals. A narrow string literal has type “array of n const char”, where n is the size of the string as defined below, and has static storage duration" and §3.7.1.1 "All variables which do not have dynamic storage duration, do not have thread storage duration, and are not local have static storage duration. The storage for these entities shall last for the duration of the program" https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf – Tommy Andersen May 05 '22 at 10:00
  • 2
    @TommyAndersen Thank you, that is an invaluable reference! – Sami Kuhmonen May 05 '22 at 10:01
14

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.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335