Pass by Value
In the strictest sense of the word, everything in C is pass-by-value. This often confuses beginning C programmers, especially when it comes to pointers, arrays, and structs. So what do we mean when we say pass-by-value and pass-by-reference.
When we pass-by-value we are passing a copy of the variable to a function. When we pass-by-reference we are passing an alias of the variable to a function. C can pass a pointer into a function but that is still pass-by-value. It is copying the value of the pointer, the address, into the function. In C++ a reference is an alias for another variable. C doesn’t have this concept.
In C when passing a pointer, the syntax requires a dereference to be applied to get the value, whereas in true pass-by-reference languages such as Pascal (parameters declared with var) and C++ (parameters declared with &), that requirement doesn’t exist. It is more correct to refer to the C mechanism as “passing a reference”. Take a look at this wiki article that further explains Pass by Reference.
Let’s look at some code to clarify all of this.
Passing Primitives to Functions
Let’s look at the classic example of a swap function for passing primitives to functions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
void swap(int a, int b); void swap(int a, int b) { int tmp = a; a = b; b = tmp; } int main() { int a = 1; int b = 2; printf("before swap a = %d\n", a); printf("before swap b = %d\n", b); swap(a, b); printf("after swap a = %d\n", a); printf("after swap b = %d\n", b); } |
Run that and the output looks like this:
before swap a = 1 before swap b = 2 after swap a = 1 after swap b = 2
The same values are printed before the swap function is called and after it is called. The swap function had no effect on our local a and b variables. Why? In pass by value, copies of the values of the are passed to the swap function. It is the copies that are swapped inside the function not the original variables.
Now lets look at a slightly different function that uses pointers.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
void swap(int *a, int *b); void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; } int main() { int a = 1; int b = 2; printf("before swap a = %d\n", a); printf("before swap b = %d\n", b); swap(&a, &b); printf("after swap a = %d\n", a); printf("after swap b = %d\n", b); } |
Run that and the output looks like this:
before swap a = 1 before swap b = 2 after swap a = 2 after swap b = 1
In this example we use the addresses of the a and b variables as input to the swap function. The output shows that the original a and b values have been successfully swapped. Even though we are using pointers, this is still pass-by-value. Copies of the pointers to the a and b variables are being passed into the function. The function itself changes the values at those addresses but it is still being passed a copy of the pointer into the function, which is pass-by-value.
Passing Structs to Functions
Passing structs are similar to passing primitives. If a struct is passed to a function the bytes of the struct are copied as the function parameter. Anything done to that struct within the function changes the copy, not the original struct.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
struct foo { int width; int height; }; void swap(struct foo input); void swap(struct foo input) { int tmp = input.width; input.width = input.height; input.height = tmp; } int main() { struct foo first; first.width = 1; first.height = 2; printf("before swap first.width = %d\n", first.width); printf("before swap first.height = %d\n", first.height); swap(first); printf("after swap first.width = %d\n", first.width); printf("after swap first.height = %d\n", first.height); } |
You should get the output:
before swap first.width = 1 before swap first.height = 2 after swap first.width = 1 after swap first.height = 2
Output shows that the original struct instance wasn’t changed. The bytes of the struct instance were copied into the function. The function operated on that copy. The original struct instance was left unchanged.
What happens when we use pointers to structs?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
struct foo { int width; int height; }; void swap(struct foo *input); void swap(struct foo *input) { int tmp = input->width; input->width = input->height; input->height = tmp; } int main() { struct foo first; first.width = 1; first.height = 2; printf("before swap first.width = %d\n", first.width); printf("before swap first.height = %d\n", first.height); swap(&first); printf("after swap first.width = %d\n", first.width); printf("after swap first.height = %d\n", first.height); } |
Now the output is different.
before swap first.width = 1 before swap first.height = 2 after swap first.width = 2 after swap first.height = 1
Using pointers we see that the values in the original struct have been successfully swapped. Similar to primitives a copy of the pointer was passed into the function. The function then uses that pointer to swap the struct member values. When using pointers to structs only the pointer value is copied and this is still pass-by-value.
Passing Arrays to Functions
When an array is passed to a function, a pointer to the address of first element of the array is copied into the function. Unlike structs, passing an array will not copy all of the bytes of the array into the fuction. Only the pointer is copied.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
void swap(int *nums); void swap(int *nums) { int tmp = *nums; int *next = nums + 1; *nums = *next; *next = tmp; } int main() { int nums[3] = {1,2}; printf("before swap nums[0] = %d\n", nums[0]); printf("before swap nums[1] = %d\n", nums[1]); swap(nums); printf("after swap nums[0] = %d\n", nums[0]); printf("after swap nums[1] = %d\n", nums[1]); } |
The output of this is:
before swap nums[0] = 1 before swap nums[1] = 2 after swap nums[0] = 2 after swap nums[1] = 1
Here we initialize an array of two int, print out the values of the indices, pass the array into the swap function, and then we reprint the values of the indices. What is being passed into the swap function is a copy of the array variable, meaning a copy of the address of the first element in the array. Using that address the swap function exchanges the values of the first and second index of the array. This is still pass-by-value as the address is what is copied into the swap function.
While there is technically a way to copy the bytes of an array as a parameter to a function, it involves initializing an array inside of a struct and then passing the struct to a function and is in my opinion a little convoluted. And even in doing that, it is still technically pass-by-value.
Conclusion
I hope this post has helped to clarify how everything is passed-by-value in C. As always I am open for comments and suggestions.
Update 1: Thanks to cryptdemon of reddit for providing links and a much clearer explanation of pass-by-reference. I have updated the article to reflect.
Update 2: Thanks to sausagefeet of reddit for providing suggestions on cleaning up the pass-by-reference section, removing the stack analogy, and word usage.