Is it safe to return a struct in C or C++?

71,729

Solution 1

It's perfectly safe, and it's not wrong to do so. Also: it does not vary by compiler.

Usually, when (like your example) your struct is not too big I would argue that this approach is even better than returning a malloc'ed structure (malloc is an expensive operation).

Solution 2

It's perfectly safe.

You're returning by value. What would lead to undefined behavior is if you were returning by reference.

//safe
mystruct func(int c, int d){
    mystruct retval;
    retval.a = c;
    retval.b = d;
    return retval;
}

//undefined behavior
mystruct& func(int c, int d){
    mystruct retval;
    retval.a = c;
    retval.b = d;
    return retval;
}

The behavior of your snippet is perfectly valid and defined. It doesn't vary by compiler. It's ok!

Personally I always either return a pointer to a malloc'ed struct

You shouldn't. You should avoid dynamically allocated memory when possible.

or just do a pass by reference to the function and modify the values there.

This option is perfectly valid. It's a matter of choice. In general, you do this if you want to return something else from the function, while modifying the original struct.

Because my understanding is that once the scope of the function is over, whatever stack was used to allocate the structure can be overwritten

This is wrong. I meant, it's sort of correct, but you return a copy of the structure you create inside the function. Theoretically. In practice, RVO can and probably will occur. Read up on return value optimization. This means that although retval appears to go out of scope when the function ends, it might actually be built in the calling context, to prevent the extra copy. This is an optimization the compiler is free to implement.

Solution 3

The lifetime of the mystruct object in your function does indeed end when you leave the function. However, you are passing the object by value in the return statement. This means that the object is copied out of the function into the calling function. The original object is gone, but the copy lives on.

Solution 4

Not only it is safe to return a struct in C (or a class in C++, where struct-s are actually class-es with default public: members), but a lot of software is doing that.

Of course, when returning a class in C++, the language specifies that some destructor or moving constructor would be called, but there are many cases where this could be optimized by the compiler.

In addition, the Linux x86-64 ABI specifies that returning a struct with two scalar (e.g. pointers, or long) values is done thru registers (%rax & %rdx) so is very fast and efficient. So for that particular case it is probably faster to return such a two-scalar fields struct than to do anything else (e.g. storing them into a pointer passed as argument).

Returning such a two-scalar field struct is then a lot faster than malloc-ing it and returning a pointer.

Solution 5

It's perfectly legal, but with large structs there are two factors that need to be taken into consideration: speed and stack size.

Share:
71,729
jzepeda
Author by

jzepeda

Updated on July 02, 2020

Comments

  • jzepeda
    jzepeda almost 4 years

    What I understand is that this shouldn't be done, but I believe I've seen examples that do something like this (note code is not necessarily syntactically correct but the idea is there)

    typedef struct{
        int a,b;
    }mystruct;
    

    And then here's a function

    mystruct func(int c, int d){
        mystruct retval;
        retval.a = c;
        retval.b = d;
        return retval;
    }
    

    I understood that we should always return a pointer to a malloc'ed struct if we want to do something like this, but I'm positive I've seen examples that do something like this. Is this correct? Personally I always either return a pointer to a malloc'ed struct or just do a pass by reference to the function and modify the values there. (Because my understanding is that once the scope of the function is over, whatever stack was used to allocate the structure can be overwritten).

    Let's add a second part to the question: Does this vary by compiler? If it does, then what is the behavior for the latest versions of compilers for desktops: gcc, g++ and Visual Studio?

    Thoughts on the matter?

  • jzepeda
    jzepeda about 12 years
    Would it still be safe if one of the fields was a char*? Now there would be pointer in the struct
  • Pablo Santa Cruz
    Pablo Santa Cruz about 12 years
    Yes, it would. Just be careful when you malloc/free that char*.
  • Luchian Grigore
    Luchian Grigore about 12 years
    Heard of return value optimization?
  • Luchian Grigore
    Luchian Grigore about 12 years
    @user963258 actually, that depends on how you implement the copy constructor and destructor.
  • Captain Giraffe
    Captain Giraffe about 12 years
    @PabloSantaCruz That is a tricky question. If it was an exam question the examiner might well expect a "no" as a response, if ownership needs to be considered.
  • Pablo Santa Cruz
    Pablo Santa Cruz about 12 years
    @CaptainGiraffe: true. Since OP didn't clarified this, and his/her examples were basically C, I assumed that it was more a C question than a C++ one.
  • Luchian Grigore
    Luchian Grigore about 12 years
    @CaptainGiraffe if I was asked this in an interview, I'd definetely say that it depends on how the two are implemented, not go for a straight "Yes".
  • ebutusov
    ebutusov about 12 years
    Yes, but we're speaking general of returning struct by value, and there are cases where the compiler is unable to perform RVO.
  • Luchian Grigore
    Luchian Grigore about 12 years
    I'd say only worry about the extra copy after you've done some profiling.
  • Kos
    Kos about 12 years
    But of course it varies by compiler! Some compilers have NRVO, some don't. And if a compiler doesn't (and by any reason you can't trash it to make place for a more recent one), then you might want to consider returning the struct by pointer "out" parameter instead.
  • Kos
    Kos about 12 years
    +1 for mentioning RVO. This important optimization actually makes this pattern feasible for objects with expensive copy constructors, like STL containers.
  • David
    David about 12 years
    @Kos Some compilers don't have NRVO? From what year? Also to note: In C++11 even if it doesn't have NRVO it will invoke move semantics instead.
  • damianostre
    damianostre about 12 years
    An original K&C "compliant" compiler doesn't support returning structs that are bigger than an "int"'s size, IIRC. @user963258: The returned struct will be copied usually over the stack, meaning that if the struct is huge, you might run out of stack space (only really relevant on embedded systems, though). The data will be copied the same way as if you'd have a "&" or "*" argument and would copy the data in your function that way. That means: if the struct contains pointers, the pointers get copied, but not what the pointers point to, of course.
  • damianostre
    damianostre about 12 years
    The stack is only used temporarily for the copy operation. Usually, the stack would get reserved before the call, and the called function puts the to-be-returned data onto the stack, and then the caller pulls this data from the stack and stores it whereever it gets assigned to. So, no worries there.
  • Necrolis
    Necrolis about 12 years
    Actually, it does vary by compiler, GCC requires an extension (under x86-32) to return structs the same as MSVC does, due to differences in the ABI used, see: gcc.gnu.org/bugzilla/show_bug.cgi?id=36834
  • Watcom
    Watcom over 11 years
    It's worth mentioning that although the compiler is free to perform return value optimization, there's no guarantee it will. This is not something you can count on, only hope.
  • Steve
    Steve over 9 years
    Would still be safe to return a vector of these structs ?
  • Jonathan Leffler
    Jonathan Leffler about 9 years
    It would be more helpful if you gave a pointer to the Wine mailing list that you're referring to.
  • M.M
    M.M over 8 years
    Returning structs is fine. COM specifies a binary interface ; if someone doesn't implement COM properly then that would be a bug.
  • Braden Best
    Braden Best almost 8 years
    Safe, sure, but with the ability to return structs, I can do retarded stuff like this: struct func { struct func (*fn)(); }; ... struct func why(){ return (struct func){why}; } ... struct func wow = why().fn().fn().fn().fn().fn().fn().fn...;, which is way more amusing than it should be. Not even a peep from -Wall -pedantic.
  • phg
    phg over 6 years
    “It is not safe to return a structure. […] the copy constructor will be called.” – There’s a difference between safe and inefficient. Returning a struct is definitely safe. Even then, the call to the copy ctor will most likely be elided by the compiler as the struct is created on the caller’s stack to begin with.
  • Lloyd Sargent
    Lloyd Sargent over 4 years
    -1 for "avoiding dynamically allocated memory when possible.” This tends to be a newb rule and frequently results in code where LARGE amounts of data is returned (and they puzzle why things run slowly) when a simple pointer can save a lot of time. The correct rule is return structures or pointers based on speed, usage, and clarity.
  • Björn Sundin
    Björn Sundin almost 4 years
    Only true before c++11.
  • Mox
    Mox almost 4 years
    interesting facts.I think golang has something similar.