5.1.7 · D5C Programming
Question bank — Functions — declaration vs definition, prototypes, call by value
True or false — justify
A function may be declared many times but defined only once.
True. Every declaration just re-states the promise (name, return type, parameter types); repeating a promise is harmless. But building the machine twice would give the linker two bodies for one name — the One Definition Rule forbids it.
A definition is also a declaration.
True. The definition contains the full signature, so the compiler learns the name, return type, and parameter types from it — everything a declaration provides — plus the body. The reverse is false: a declaration alone has no body.
Parameter names are required in a prototype.
False.
int add(int, int); is fully valid. The compiler only needs the types to check a call; the names in a prototype are documentation for humans and are ignored.Call by value means the function can permanently change the variable you passed.
False. It receives a copy of the value in its own stack frame. Editing that copy leaves the caller's original untouched, unless you passed an address (see Pointers in C).
Passing a pointer to a function is not call by value.
False. It is call by value — the address (a number) is copied into the parameter. But since that copied number still points to the original data, dereferencing (
*p) reaches the real variable.In C, the order of a function's definition relative to main never matters.
False. The classic C compiler reads top-to-bottom once. If you call before it has seen either a prototype or the definition, it has no signature to check against — an implicit-declaration error/warning.
Two identical prototypes for the same function cause a compile error.
False. Redeclaring is allowed as long as the declarations agree. Repeating the same promise is fine; only a second definition is illegal.
Changing a parameter's value inside a function is a code smell that leaks out to the caller.
False. A parameter behaves like a fresh local variable initialised from the argument. Reassigning it (
x = x + 1) is perfectly safe and invisible to the caller — see Scope and Lifetime of Variables.void f(void) and void f() mean the same thing in C.
False (in C).
void f(void) promises no parameters, so f(1) is an error. void f() is an old-style declaration meaning "parameters unspecified", which disables argument checking — a trap. Always write void.Spot the error
Why is this a bug? int add(double a, double b); ... int add(int a, int b){ return a+b; }
The prototype and the definition disagree on parameter types. In one file the compiler complains; across separate files the linker may silently accept it and calls get miscompiled. The two lines must be identical.
What's wrong here? int main(){ int s = add(3,4); } int add(int a,int b){return a+b;}
add is called before any declaration or definition appears. The single-pass compiler hits add(3,4) with no known signature. Fix: put a prototype int add(int,int); above main, or define add first.Find the mistake: void swap(int x,int y){int t=x;x=y;y=t;} ... swap(a,b); expecting a,b to swap.
swap only ever touches its copies x and y. The caller's a and b live in a different frame and are never reached. To really swap, pass addresses: void swap(int *x,int *y) and call swap(&a,&b).Why won't this compile as intended? int square(int n) return n*n;
A function definition's body must be enclosed in braces
{ ... }. Without them there's no valid body; the compiler cannot tell where the function's code begins and ends.What's wrong? A header .h file contains int add(int a,int b){ return a+b; }.
A header should carry the declaration (prototype), not the definition. If two
.c files include this header, the body gets compiled into both, giving the linker duplicate definitions — a One Definition Rule violation. See Header Files and Separate Compilation.Spot the trap: int get(){ ... } then later char x = get('A');.
The old-style
() disables argument checking, so passing 'A' isn't caught, and the declared return int is silently narrowed into char x. Two invisible mismatches. Use explicit parameter lists and matching types.Why questions
Why does C insist on a prototype before the call rather than figuring it out later?
The classic compiler is single-pass — it processes each line once and can't look ahead. To check argument count/types and convert them correctly at the call site, it must already hold the signature.
Why did pre-prototype C default to assuming a function returns int?
Early C had no prototypes, so when it met an unknown function it guessed
int return and applied default argument promotions. This "implicit int" rule is a bug factory — the prototype exists precisely to remove the guessing.Why is call by value considered safe for pure computations like square(v)?
The function works on a private copy, so it cannot accidentally corrupt the caller's variables. The result is predictable:
v stays exactly as it was regardless of what the function does internally.Why must a function be defined exactly once but can be declared many times?
A declaration is only a promise about the interface; many identical promises are consistent. A definition is the actual code and storage — two bodies for one name would make the call ambiguous and the linker fail.
Why is passing a pointer still "call by value" even though the original changes?
What gets copied is the address, not the pointed-to data. The copy is a value like any other; it just happens to be a value that names a location, so dereferencing it reaches back to the original.
Why does splitting declaration from definition let big programs span many files?
Each
.c file only needs the promise (declaration, usually via a header) to compile calls; the actual body can live in another file compiled separately and joined by the linker. See Header Files and Separate Compilation.Edge cases
What happens when you call a function with no arguments and declare it int f(void) vs int f()?
With
int f(void) the compiler enforces zero arguments — f(5) is rejected. With int f() argument checking is off, so f(5) compiles silently. Prefer void to keep the safety net.What if a function's parameter and the caller's variable have the same name?
They are still separate memory in separate frames. The name coincidence is cosmetic; the parameter is a fresh local initialised from the argument's copied value, governed by Scope and Lifetime of Variables.
What happens to the stack frame's copies when the function returns?
The frame — parameters and locals — is destroyed. That is why changes to copies can't leak: the scratchpad holding them no longer exists after return.
An array passed to a function — is that a copy of the whole array (call by value)?
No. In C an array argument decays to a pointer to its first element, so only that address is copied. The function can therefore modify the original array elements — see Arrays as Function Arguments.
Does a recursive function get one shared copy of its parameters, or a new copy each call?
A new stack frame with fresh parameter copies is created for every call. That independence is exactly what lets each level of Recursion hold its own values without clobbering the others.
What is the return value of a function whose call is swap(&a,&b) but whose prototype was void swap(int,int)?
The types mismatch — you're passing
int* where int is promised. The compiler flags a type error at the call. Prototype and use must agree; here the prototype should read void swap(int*, int*).Recall One-line litmus test for any trap here
Ask: "Is a copy being made, and of what — a value or an address?" Almost every trap on this page resolves the moment you name what was copied.
Connections
- Pointers in C — the escape hatch from call by value.
- Stack and Function Call Mechanism — why copies vanish on return.
- Header Files and Separate Compilation — declaration vs definition across files.
- Arrays as Function Arguments — the array-decay exception.
- Recursion — fresh copies per call.
- Scope and Lifetime of Variables — why same-named variables stay separate.