1

Here is a C function that returns an array with a type defined in a struct. It successfully returns the array p. My question: can I eliminate p and do a return as shown in the commented-out statement?

#include<stdio.h>

typedef struct {
    int dy, dx, iter;
} M;
  
M *boom (int x) {

    // I'm happy with this approach
    static M p[] = { {+5, +6, +7}, {+1, +1, +0}, {+3, +3, +3} };
    return p;
    // but I'm keen to know how this can be done.
//    return { {+5, +6, +7}, {+1, +1, +0}, {+3, +3, +3} };  // causes error
}

int main() {
    M *result = boom(1);
    printf("First element %d\n", result[2].dy);
}

I get "error: expected expression before '{' token" for the line I've commented out.

Fred
  • 564
  • 10
  • 21

3 Answers3

3

No you cannot.

The closest I can think if that you can return a compound literal. Example:

struct myStruct {
    int x;
    int y;
};

struct myStruct foo() {
    return (struct MyStruct) {.x =5, .y=6};
}

But you cannot do this with arrays. As Eric Postpischil said in the comments:

To be clear, this is not because compound literals cannot be arrays. They can. It is because an array cannot be returned by value, and its address, or the address of its first element, cannot properly be returned because the array’s lifetime would end with the function return.

Something that is possible if you restrict yourself to fixed size arrays is to wrap the array in a struct:

struct myStruct {
    int x;
    int y;
};

struct myContainer {
    struct myStruct arr[3];
};

struct myContainer foo() {
    return (struct myContainer) { 
        .arr={{.x =5, .y=6}, {.x=4, .y=1}, {.x=42, .y=665}}
    };
}

I might also mention that even if returning addresses to local static variables is perfectly legal, it is widely considered a strong code smell.

klutt
  • 30,332
  • 17
  • 55
  • 95
  • Only note that [compound literals](https://en.cppreference.com/w/c/language/compound_literal) require a C99 or later standard compiler (+1). – dxiv Nov 25 '20 at 19:48
  • @dxiv Yeah, but that's like saying "note that you need a graphics card that supports at least 256 colors to play this game" :D – klutt Nov 25 '20 at 19:51
  • @klutt Except MSVC waited until [2013](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2013/hh409293(v=vs.120)) to support compound literals in C ;-) – dxiv Nov 25 '20 at 19:58
  • @dxiv I have to admit that you have a point. But still, if someone tries this with a pre C99 compiler, they will immediately notice that something is wrong. And I think they will post a question here irregardless if I add that info or not. ;) – klutt Nov 25 '20 at 20:08
  • @dxiv: Microsoft does not have a C compiler. MSVC is a C++ compiler with some C features. It should generally not be considered relevant to C discussions. – Eric Postpischil Nov 25 '20 at 20:11
  • @EricPostpischil It is officially a C compiler since [Sept 14, 2020](https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-support-arriving-in-msvc/) ;-) – dxiv Nov 25 '20 at 20:13
  • @dxiv I would hesitate to call it a C compiler anyway. How does it handle those cases where a piece of code is both valid C and valid C++, but would produce different results depending on what language you compile it as? I highly suspect that it would compile it as C++ then. – klutt Nov 25 '20 at 20:17
  • @dxiv For instance, what would the result of this be? `printf("%zu", sizeof('a'));`? – klutt Nov 25 '20 at 20:18
  • @klutt All I said is that the claim is out there. If it doesn't do the right thing it should (and likely was or will) be reported as a compliance error. – dxiv Nov 25 '20 at 20:26
1

Array literals can only be used for initialization. They can't be passed around like string literals or integers or floats.

fpf3
  • 421
  • 6
  • 11
  • 1
    The initializers in `static M p[] = { {+5, +6, +7}, {+1, +1, +0}, {+3, +3, +3} };` are not an array literal. They are simply a list of initializers, per C 2018 6.7.9. So does have compound literals, and a compound literal can be an array, but it cannot properly be returned from a function, as its lifetime would end with the function return. – Eric Postpischil Nov 25 '20 at 19:51
1

No, but you can return a pointer to any construct, including arrays and structs using identical approaches. eg:

Given the prototype M *boom (int x), the following demonstrates how you can pass back an instance of M struct data after having modified it.

typedef struct {
    int dy, dx, iter;
} M;
  
M *boom (int x) {

    M *new = malloc(sizeof(*new));
    if(new)
    {
        //modify new instance of M
        new->dx = x;
        //and so on
    }

    return new;
}

int main() {
    M *result = boom(1);
    printf("First element %d\n", result[2].dy);
    free(result);
}  

This method can be used exactly the same way with arrays:

Given the following function:

int * func(int *array, size_t size, int index, int newValue)
{
     int *newArray = malloc(size * sizeof(*newArray));
     //modify as much of original array as needed:
     newArray[index] = newValue;
     return newArray;
}    

In main():

int *a = func(array, sizeof(array), 2, 100);
printf("new value for element %d is %d\n", 2, a[2]);
free(a);
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • @Bob Jones - You deleted a question [here](https://stackoverflow.com/q/65045203/645128), and thus an answer, that I believe would have been useful to many people. Please consider re-opening. – ryyker Nov 28 '20 at 00:27