Return a `struct` from a function in C

C

C Problem Overview


Today I was teaching a couple of friends how to use C structs. One of them asked if you could return a struct from a function, to which I replied: "No! You'd return pointers to dynamically malloced structs instead."

Coming from someone who primarily does C++, I was expecting not be able to return structs by values. In C++ you can overload the operator = for your objects and makes complete sense to have a function to return your object by value. In C, however, you do not have that option and so it got me thinking what the compiler is actually doing. Consider the following:

struct MyObj{
    double x, y;
};

struct MyObj foo(){
    struct MyObj a;
    
    a.x = 10;
    a.y = 10;
    
    return a;
}        

int main () {

    struct MyObj a;
    
    a = foo();    // This DOES work
    struct b = a; // This does not work
      
    return 0;
}    

I understand why struct b = a; should not work -- you cannot overload operator = for your data type. How is it that a = foo(); compiles fine? Does it mean something other than struct b = a;? Maybe the question to ask is: What exactly does the return statement in conjunction to = sign do?

C Solutions


Solution 1 - C

You can return a structure from a function (or use the = operator) without any problems. It's a well-defined part of the language. The only problem with struct b = a is that you didn't provide a complete type. struct MyObj b = a will work just fine. You can pass structures to functions as well - a structure is exactly the same as any built-in type for purposes of parameter passing, return values, and assignment.

Here's a simple demonstration program that does all three - passes a structure as a parameter, returns a structure from a function, and uses structures in assignment statements:

#include <stdio.h>

struct a {
   int i;
};

struct a f(struct a x)
{
   struct a r = x;
   return r;
}

int main(void)
{
   struct a x = { 12 };
   struct a y = f(x);
   printf("%d\n", y.i);
   return 0;
}

The next example is pretty much exactly the same, but uses the built-in int type for demonstration purposes. The two programs have the same behaviour with respect to pass-by-value for parameter passing, assignment, etc.:

#include <stdio.h>

int f(int x) 
{
  int r = x;
  return r;
}

int main(void)
{
  int x = 12;
  int y = f(x);
  printf("%d\n", y);
  return 0;
}

Solution 2 - C

When making a call such as a = foo();, the compiler might push the address of the result structure on the stack and passes it as a "hidden" pointer to the foo() function. Effectively, it could become something like:

void foo(MyObj *r) {
    struct MyObj a;
    // ...
    *r = a;
}

foo(&a);

However, the exact implementation of this is dependent on the compiler and/or platform. As Carl Norum notes, if the structure is small enough, it might even be passed back completely in a register.

Solution 3 - C

The struct b line doesn't work because it's a syntax error. If you expand it out to include the type it will work just fine

struct MyObj b = a;  // Runs fine

What C is doing here is essentially a memcpy from the source struct to the destination. This is true for both assignment and return of struct values (and really every other value in C)

Solution 4 - C

As far as I can remember, the first versions of C only allowed to return a value that could fit into a processor register, which means that you could only return a pointer to a struct. The same restriction applied to function arguments.

More recent versions allow to pass around larger data objects like structs. I think this feature was already common during the eighties or early nineties.

Arrays, however, can still be passed and returned only as pointers.

Solution 5 - C

yes, it is possible we can pass structure and return structure as well. You were right but you actually did not pass the data type which should be like this struct MyObj b = a.

Actually I also came to know when I was trying to find out a better solution to return more than one values for function without using pointer or global variable.

Now below is the example for the same, which calculate the deviation of a student marks about average.

#include<stdio.h>
struct marks{
    int maths;
    int physics;
    int chem;
};

struct marks deviation(struct marks student1 , struct marks student2 );

int main(){
	
	struct marks student;
	student.maths= 87;
	student.chem = 67;
	student.physics=96;
	
	struct marks avg;
	avg.maths= 55;
	avg.chem = 45;
	avg.physics=34;
	//struct marks dev;
	struct marks dev= deviation(student, avg );
	printf("%d %d %d" ,dev.maths,dev.chem,dev.physics);

    return 0;
 }

struct marks deviation(struct marks student , struct marks student2 ){
    struct marks dev;

    dev.maths = student.maths-student2.maths;
    dev.chem = student.chem-student2.chem;
    dev.physics = student.physics-student2.physics;	

    return dev;
}

Solution 6 - C

There is no issue in passing back a struct. It will be passed by value

But, what if the struct contains any member which has a address of a local variable

struct emp {
    int id;
    char *name;
};

struct emp get() {
    char *name = "John";

    struct emp e1 = {100, name};

    return (e1);
}

int main() {

    struct emp e2 = get();

    printf("%s\n", e2.name);
}

Now, here e1.name contains a memory address local to the function get(). Once get() returns, the local address for name would have been freed up. SO, in the caller if we try to access that address, it may cause segmentation fault, as we are trying a freed address. That is bad..

Where as the e1.id will be perfectly valid as its value will be copied to e2.id

So, we should always try to avoid returning local memory addresses of a function.

Anything malloced can be returned as and when wanted

Solution 7 - C

You can assign structs in C. a = b; is valid syntax.

You simply left off part of the type -- the struct tag -- in your line that doesn't work.

Solution 8 - C

struct emp {
    int id;
    char *name;
};

struct emp get() {
    char *name = "John";

    struct emp e1 = {100, name};

    return (e1);
}

int main() {

    struct emp e2 = get();

    printf("%s\n", e2.name);
}

works fine with newer versions of compilers. Just like id, content of the name gets copied to the assigned structure variable.

Solution 9 - C

struct var e2 address pushed as arg to callee stack and values gets assigned there. In fact, get() returns e2's address in eax reg. This works like call by reference.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmmirzadehView Question on Stackoverflow
Solution 1 - CCarl NorumView Answer on Stackoverflow
Solution 2 - CGreg HewgillView Answer on Stackoverflow
Solution 3 - CJaredParView Answer on Stackoverflow
Solution 4 - CGiorgioView Answer on Stackoverflow
Solution 5 - CAmanView Answer on Stackoverflow
Solution 6 - CJaganView Answer on Stackoverflow
Solution 7 - CDigitalRossView Answer on Stackoverflow
Solution 8 - Csaroj pandaView Answer on Stackoverflow
Solution 9 - CBalaView Answer on Stackoverflow