Reputation: 51
I have probably read hundreds of times the difference between pass-by value and pass-by reference. Pass by reference makes sense because given the address of memory we can directly change what's in there. But pass by value still eludes me.
For example, in here it says that To pass by value means that the argument (a variable, constant or other expression) is evaluated and a copy of its value is then passed to the function.
.
As far as I know local variables go on stack in the C memory model. Does this mean that if I pass by value a struct with hundred fields each field will be added on top of the stack in order to make a copy?
Upvotes: 0
Views: 2862
Reputation: 15788
Pass by value (also "call by value") means that values passed to functions as function parameters have their values copied (new memory is created to store them for the duration of the function call) - the function's parameters are not aliases or references to memory that holds the thing the caller provided - its strictly a copy of its value.
For example, consider this pseudo code.
func f(x):
x += 1
y = 1
f(y)
print(y)
In languages with pass-by-reference semantics 2 is printed - x is a reference/alias to the memory that holds the data of variable, y. f
changes the value of the thing the caller provided, for the caller of the function.
With pass-by-value semantics 1 is printed, because a copy of the value of y is provided as the parameter to the function. The new memory for this new data is typically placed on a stack, and destroyed/released/recycled when f returns.
So, pass-by-value/call-by-value and call-by-reference are types of semantics for a function call - they define what the function's parameters mean to the caller of the function and within the function. This a is a property that is independent of compiler optimizations, the size of data provided, and the actual details of how a program is executed in practice.
But to go into detail in C, specifically:
void f(int x) {
x += 1;
}
...
int y = 1;
f(y);
printf("%d", y); // "1"
.. in the above the data for the integer x
is made to be a copy of the value of y
provided by the caller. Therefore, when the value is modified within the function, the original copy does not see a change. It doesn't matter what the type of the parameter is, or how large it is, or whether it is a complex data structure, in call/pass-by-value, a function works on a copy of its parameter.
Note that C is pass-by-value even though it has pointers. All this means is that a copies of the values of memory pointers are passed as the values to functions, for functions with pointer arguments. This doesn't prevent you from referencing memory and even changing memory created outside of the scope of a C function, but it does mean that the new pointer can have its addressed value modified within the scope of the function:
void f(int* x) {
printf("X1 %p", x); // Prints a COPY of a memory address that points to data of int y
// *x += 99; // Uncomment to modify the value of y
printf("X val %d", *x); // The value of y
x += 1; // The next memory address after the value of y ?!!!!
printf("X2 %d", x); // Address of y + sizeof(int)
// Look. We modified a copy of a memory address.
// y_ptr is NOT modified - call-by-value means a copy was provided.
}
...
int y = 1;
int* y_ptr = &y;
printf("Y1 %p", y_ptr); // Memory location of the data for y.
f(&y);
printf("Y2 %p", y_ptr); // Same as Y1
printf("Y %d", y); // 1, if we didn't crash yet
Now, to answer your specific question about structs in C:
#include <stdio.h>
struct S {
int a;
int b;
};
void printS(struct S z){
printf("s.a=%d s.a=%d\n", z.a, z.b);
}
void f(struct S z){
z.a = 98;
z.b = 99;
printS(z); // s.a=98 s.a=99
}
int main() {
struct S x = {123, 456};
f(x);
printS(x); // s.a=123 s.a=456
return 0;
}
Two sets of values. f
doesn't modify x
. Structs, therefore, have pass-by-value semantics, just like primitive types.
You'll have to take my word for it that a second copy of the struct is typically added on a stack. At least, this is how you should understand the meaning of your program.
(If you really care, you can test this by printing the address of the struct in main
and in f
.)
Note, pass-by-value is a term that is only about program semantics, not about implementation. The C language is described only by a virtual machine which in turn specifies the defined lifetime of storage for data, including the memory of function parameters. The C specification doesn't really talk about stacks, or pass-by-value, and certainly has nothing to say about processors and their stack registers. There's no answer for how pass-by-value is implemented in practice, since the implementation is unspecified. Some C compilers don't even define an ABI. If you want an answer, you need to say which implementation - which compiler and even which parameters to it.
However, C does define the abstract memory model of an abstract stack as the only possible consequence of having call by value semantics, and call by value semantics as the consequence of specifying that function parameters shall have defined addressable storage holding copies of data for the lifetime of a function call. The programmer should rely upon the abstract stack model, because those are the program semantics that the C specification provides. Where tools chains and optimizers reveal by their implementation for there not to be a stack, you should probably consider this to be an implementation bug in your C compiler tools.
So ... is there a stack in practice, and are structs copied via the program's call stack?
Well... it depends on your C compiler toolchain.
With fancy, rootin' tootin' high falootin' catch-of-the-day modern optimizers, whether a new struct is actually placed on the stack may tricky to know. In the above example, a good optimizer may able to go straight to: 'oh -- the semantics of your program was to print s.a=98 s.a=99
and then s.a=123 s.a=456
, then that's what I'll do and skip creating any memory for any structs at all' - there is no spoon.
Upvotes: 2
Reputation: 223503
As far as I know local variables go on stack in the C memory model. Does this mean that if I pass by value a struct with hundred fields each field will be added on top of the stack in order to make a copy?
How arguments are passed can vary. Each computing platform has an application binary interface (ABI) that specifies how arguments are passed, and C implementations typically use the target platform’s ABI, at least for calling functions with external linkage. ABIs for modern general-purpose processors like those in consumer notebooks and desktops typically say the first few arguments of modest sizes are passed in processor registers and other arguments are passed on the stack. (There are embellishments to this, involving structures with diverse or uniform members, alignment issues, SIMD features, and so on.)
If you pass a large structure as an argument, it will be typically passed on the stack, and this may involve a lot of otherwise unnecessary copying of structure members. If you are fortunate, and the compiler can see the definition of the function while it is compiling the call to the function, it might optimize this part of the program by “inlining” the function. This means that, instead of actually calling the function, it builds the code of the called function into the calling function. This, and the subsequent optimization on the integrated code, might eliminate some or all of the copying of the structure, depending on circumstances.
Nonetheless, it should generally be avoided. If a function needs to read data from a large structure, it is preferable to declare a parameter that is a pointer to a const
-qualified type of that structure.
(There is a related issue with returning a value: A function can also return a structure. The way this is commonly implemented, and specified in ABIs, is that there is an extra parameter passed to the function in addition to those declared in the source code. When a function is calling a function that returns a structure, the calling function sets aside space for the structure and passes the address of that space in the extra parameter. Then the called function stores the return value in that space.)
Upvotes: 3