Are there any downsides to passing structs by value in C, rather than passing a pointer?
If the struct is large, there is obviously the performancd aspect of copying
One thing people here have forgotten to mention so far (or I overlooked it) is that structs usually have a padding!
struct {
short a;
char b;
short c;
char d;
}
Every char is 1 byte, every short is 2 bytes. How large is the struct? Nope, it's not 6 bytes. At least not on any more commonly used systems. On most systems it will be 8. The problem is, the alignment is not constant, it's system dependent, so the same struct will have different alignment and different sizes on different systems.
Not only that padding will further eat up your stack, it also adds the uncertainty of not being able to predict the padding in advance, unless you know how your system pads and then look at every single struct you have in your app and calculate the size for it. Passing a pointer takes a predictable amount of space -- there is no uncertainty. The size of a pointer is known for the system, it is always equal, regardless of what the struct looks like and pointer sizes are always chosen in a way that they are aligned and need no padding.
I'd say passing (not-too-large) structs by value, both as parameters and as return values, is a perfectly legitimate technique. One has to take care, of course, that the struct is either a POD type, or the copy semantics are well-specified.
Update: Sorry, I had my C++ thinking cap on. I recall a time when it was not legal in C to return a struct from a function, but this has probably changed since then. I would still say it's valid as long as all the compilers you expect to use support the practice.
Simple solution will be return an error code as a return value and everything else as a parameter in the function,
This parameter can be a struct of course but don't see any particular advantage passing this by value, just sent a pointer.
Passing structure by value is dangerous, you need to be very careful what are you passing are, remember there is no copy constructor in C, if one of structure parameters is a pointer the pointer value will be copied it might be very confusing and hard to maintain.
Just to complete the answer (full credit to Roddy ) the stack usage is another reason not pass structure by value, believe me debugging stack overflow is real PITA.
Replay to comment:
Passing struct by pointer meaning that some entity has an ownership on this object and have a full knowledge of what and when should be released. Passing struct by value create a hidden references to the internal data of struct (pointers to another structures etc .. ) at this is hard to maintain (possible but why ?) .
Page 150 of PC Assembly Tutorial on http://www.drpaulcarter.com/pcasm/ has a clear explanation about how C allows a function to return a struct:
C also allows a structure type to be used as the return value of a func- tion. Obviously a structure can not be returned in the EAX register. Different compilers handle this situation differently. A common solution that compilers use is to internally rewrite the function as one that takes a structure pointer as a parameter. The pointer is used to put the return value into a structure defined outside of the routine called.
I use the following C code to verify the above statement:
struct person {
int no;
int age;
};
struct person create() {
struct person jingguo = { .no = 1, .age = 2};
return jingguo;
}
int main(int argc, const char *argv[]) {
struct person result;
result = create();
return 0;
}
Use "gcc -S" to generate assembly for this piece of C code:
.file "foo.c"
.text
.globl create
.type create, @function
create:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl 8(%ebp), %ecx
movl $1, -8(%ebp)
movl $2, -4(%ebp)
movl -8(%ebp), %eax
movl -4(%ebp), %edx
movl %eax, (%ecx)
movl %edx, 4(%ecx)
movl %ecx, %eax
leave
ret $4
.size create, .-create
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $20, %esp
leal -8(%ebp), %eax
movl %eax, (%esp)
call create
subl $4, %esp
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
The stack before call create:
+---------------------------+
ebp | saved ebp |
+---------------------------+
ebp-4 | age part of struct person |
+---------------------------+
ebp-8 | no part of struct person |
+---------------------------+
ebp-12 | |
+---------------------------+
ebp-16 | |
+---------------------------+
ebp-20 | ebp-8 (address) |
+---------------------------+
The stack right after calling create:
+---------------------------+
| ebp-8 (address) |
+---------------------------+
| return address |
+---------------------------+
ebp,esp | saved ebp |
+---------------------------+