Parameters and Return Values
C passes every argument by value: the function receives a copy. That simple rule explains why a function can't change your variables directly, how arrays seem to be an exception, and how C functions "return" several results through pointer parameters.
Arguments are copies
#include <stdio.h> void try_to_double(int n) { n = n * 2; printf("inside: %d\n", n); } int main(void) { int value = 21; try_to_double(value); printf("outside: %d\n", value); return 0; }
inside: 42 outside: 21
The function doubled its own copy. To change the caller's value, either return the new value, or pass the variable's address so the function can write through it.
Return the result
#include <stdio.h> int doubled(int n) { return n * 2; } int main(void) { int value = 21; value = doubled(value); printf("%d\n", value); return 0; }
42
When a function produces one value, returning it is the clearest design.
Several results: pointer parameters
A function can only return one value. To produce more, the caller passes the addresses of its variables (with &), and the function stores through them (with *). Pointers get a full lesson later (31); here's the pattern:
#include <stdio.h> void split_time(int total_seconds, int *minutes, int *seconds) { *minutes = total_seconds / 60; *seconds = total_seconds % 60; } int main(void) { int m, s; split_time(754, &m, &s); printf("754 s = %d min %d s\n", m, s); return 0; }
754 s = 12 min 34 s
This is exactly how scanf stores what it reads into your variables.
Returning a status and a result
A very common C convention: return whether the operation succeeded, and deliver the actual result through a pointer:
#include <stdio.h> #include <stdbool.h> bool safe_divide(int a, int b, int *result) { if (b == 0) { return false; } *result = a / b; return true; } int main(void) { int q; if (safe_divide(17, 5, &q)) printf("17 / 5 = %d\n", q); if (!safe_divide(17, 0, &q)) printf("17 / 0 is not allowed\n"); return 0; }
17 / 5 = 3 17 / 0 is not allowed
Arrays as parameters
An array argument isn't copied: the function receives a pointer to its first element (lesson 32 explains why). So the function can change the caller's array, and it can't know the array's length — you pass that separately:
#include <stdio.h> void scale(double values[], size_t count, double factor) { for (size_t i = 0; i < count; i++) { values[i] *= factor; } } double sum(const double values[], size_t count) { double total = 0; for (size_t i = 0; i < count; i++) total += values[i]; return total; } int main(void) { double prices[] = { 10.0, 4.5, 20.0 }; scale(prices, 3, 1.1); printf("%.2f %.2f %.2f, sum %.2f\n", prices[0], prices[1], prices[2], sum(prices, 3)); return 0; }
11.00 4.95 22.00, sum 37.95
const in sum promises not to modify the array — and the compiler enforces it. Mark array and pointer parameters const whenever the function only reads them (lesson 35).
Argument conversions
With a prototype in scope, arguments are converted to the parameter types, just like assignment. Passing 7 to a double parameter gives 7.0; passing 3.9 to an int parameter truncates to 3 (and -Wconversion would warn).
Structs by value
Structures (lesson 40) are copied too when passed by value. For large structs, passing a pointer — usually a const pointer — avoids copying all of the data (lesson 41).
Try it yourself
Write min_max, which finds the smallest and largest values in an array and delivers both through pointer parameters.
min 6, max 21