Search code examples
cmemorystructreturnc99

Do arrays in struct fields always persist upon return?


I know that I can't return a locally declared array from a function, because the array lives in the function's stack space and after returning, the pointer will dangle.

I.e. this is invalid

int* test() {
    int x[3] = {1,2,3};
    return x;
}

main() {
    int* x = test();
}

Therefore I'm nervous about returning structs which contain arrays; since they are just made part of the struct's contiguous memory space, are copied on return.

I.e. this is totally fine

typedef struct Container {
    int arr[3][3];
} Container;
Container getContainer() {
    Container c = {.arr = {{1,2,3}, {4,5,6}, {7,8,9}} };
    return c;
}

int main() {
    Container c = getContainer();
    // c.arr is "deep copied" to main's stack space
}

Still, I have a deep instinct to instead do do something like

void populateContainer(Container* c) {
    int arr[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}};
    memcpy(c->arr, arr, 3*3 * sizeof(arr));
}

int main() {
    Container c;
    populateContainer(&c);
}

So my question is, should I just trust that the array will always be safely copied-by-value when the struct is returned, and avoid the latter pattern? I'm always using the C99 standard. Are there compilers which wouldn't respect this, and for which I should use the uglier-but-seemingly-safer address-passing pattern?


Solution

  • As per 6.9.1p3:

    The return type of a function shall be void or an object type other than array type.

    Regarding what an object type is defined to be, from 6.2.5p1:

    The meaning of a value stored in an object or returned by a function is determined by the type of the expression used to access it. (An identifier declared to be an object is the simplest such expression; the type is specified in the declaration of the identifier.) Types are partitioned into object types (types that fully describe objects), function types (types that describe functions), and incomplete types (types that describe objects but lack information needed to determine their sizes).

    Also see this question about object types.


    In conclusion, since this is a valid object type:

    struct Container {
        int arr[3][3];
    };
    

    If your compiler is compliant with the standard, you can expect Container c = getContainer(); to work even when the Container is created inside the stack of the function and then returned, since the standard does not specify the origin of the returned value.