A C function declares a return type, a name, and a parameter list; void means it returns nothing. Every argument is passed by value — the function receives a copy, and modifying that copy inside the function never affects the caller's original variable. The only way to let a function modify a caller's data is to explicitly pass a pointer to it, so the function can dereference and write through that address. If a function's definition appears later in the file than its first call, a prototype declaring its signature must appear earlier, or the compiler has no way to check the call is correct.
Everything is pass-by-value
double() receives its own copy of x, so multiplying it has zero effect on the caller's original variable — this is true for every C function call, with no exceptions.
void double_it(int n) {
n = n * 2; // modifies only the LOCAL copy
}
int main(void) {
int x = 5;
double_it(x);
printf("%d\n", x); // still 5 — double_it never touched the original
return 0;
}Passing a pointer to actually modify the caller's variable
Passing &x (x's address) lets the function dereference that address and write through it directly — this is C's only mechanism for a function to affect the caller's own data.
void double_it(int *n) {
*n = *n * 2; // dereference: modifies the ORIGINAL, through its address
}
int main(void) {
int x = 5;
double_it(&x);
printf("%d\n", x); // 10 — the original WAS modified, via the pointer
return 0;
}Function prototypes: declare before use
Without the prototype, calling add() before its definition appears would be a compile error — the compiler needs to know a function's signature before checking any call to it, and a prototype supplies exactly that without needing the full definition yet.
int add(int a, int b); // prototype: declares the signature
int main(void) {
printf("%d\n", add(2, 3)); // fine — the prototype above already declared add()
return 0;
}
int add(int a, int b) { // the actual definition, further down the file
return a + b;
}