C Function Parameters
Function parameters are the named inputs a C function receives when it is called. They matter because they let one function work with different values each time instead of depending on fixed data inside the function body.
In C, parameters are strongly typed and, by default, receive copies of the arguments passed by the caller. Understanding that copy behavior is the key to using parameters correctly, especially when you want a function to change a variable, process an array, or keep an interface clear.
Overview: How Function Parameters Work
A parameter is the variable written in a function declaration or definition. An argument is the actual value or expression supplied in a function call. In printAge(21), age might be the parameter and 21 is the argument.
Each parameter has a type and a name in the function definition. When the function is called, C evaluates each argument, converts it to the parameter type when allowed, and initializes the parameter with that value. The parameter then behaves like a local variable inside the function. It exists for that function call and disappears when the function returns.
The most important rule is that C passes arguments by value. That means the function receives a copy of the caller’s value, not the caller’s variable itself. If a function parameter is int points and the function writes points = 100;, only the local copy changes. The original variable in main stays the same.
To let a function modify a caller’s variable, pass the variable’s address and receive it through a pointer parameter. A parameter such as int *value can point to an int owned by the caller. The function can then use *value to read or write the caller’s object. This is still pass-by-value: the pointer itself is copied, but both the original pointer value and the copied pointer value refer to the same memory address.
Arrays are a special-looking case. When you pass an array to a function, the array expression is converted to a pointer to its first element. A parameter written as int numbers[] is adjusted by the compiler to int *numbers. Because the function receives a pointer to the first element, it can modify array elements, but it does not automatically know the array length. You usually pass the length as a separate parameter.
Parameters also form part of a function’s contract. The function declaration, also called a prototype, must agree with the function definition: same return type and compatible parameter types. Names in a prototype are optional, but types are required because the compiler uses them to check calls.
Syntax
This complete program shows the general form of a function with parameters, a prototype, a call with arguments, and a matching definition:
#include <stdio.h>
int add(int left, int right);
int main(void)
{
printf("Sum: %d\n", add(2, 3));
return 0;
}
int add(int left, int right)
{
return left + right;
}
Output:
Sum: 5
In a real program, the names and types should describe the values being passed:
#include <stdio.h>
void printRectangleArea(int width, int height);
int main(void)
{
printRectangleArea(6, 4);
return 0;
}
void printRectangleArea(int width, int height)
{
printf("Area: %d\n", width * height);
}
Output:
Area: 24
| Part | Meaning |
|---|---|
int left |
Declares one local parameter variable and the type of argument it expects. |
, |
Separates multiple parameters. Each parameter needs its own type. |
add(2, 3) |
Calls the function and supplies the argument values. |
void |
In a parameter list, means the function accepts no arguments, as in int getCount(void). |
* |
Declares a pointer parameter, often used when the function needs to modify caller data or avoid copying large data. |
Examples
Passing simple values
#include <stdio.h>
void printScore(char initial, int score)
{
printf("Student %c scored %d\n", initial, score);
}
int main(void)
{
printScore('A', 92);
printScore('B', 85);
return 0;
}
Output:
Student A scored 92
Student B scored 85
The function printScore has two parameters: a char and an int. Each call supplies different arguments, so the same function body prints different results. The parameter names are local to printScore; main does not have variables named initial or score.
Returning a result from parameters
#include <stdio.h>
double calculateTotal(double price, int quantity)
{
return price * quantity;
}
int main(void)
{
double apples = calculateTotal(1.25, 4);
double oranges = calculateTotal(0.80, 6);
printf("Apples: %.2f\n", apples);
printf("Oranges: %.2f\n", oranges);
return 0;
}
Output:
Apples: 5.00
Oranges: 4.80
This function accepts a price and a quantity, then returns their product. Parameters are often better than reading global variables because the function’s needed inputs are visible in its header. The caller can also use the returned value however it wants: store it, print it, compare it, or pass it to another function.
Pass-by-value does not change the caller
#include <stdio.h>
void resetCopy(int value)
{
value = 0;
printf("Inside resetCopy: %d\n", value);
}
int main(void)
{
int count = 7;
resetCopy(count);
printf("After call: %d\n", count);
return 0;
}
Output:
Inside resetCopy: 0
After call: 7
The argument count is copied into the parameter value. The assignment value = 0; changes only that local copy. When the function returns, the original count in main is still 7.
Using a pointer parameter to modify a variable
#include <stdio.h>
void resetValue(int *value)
{
*value = 0;
}
int main(void)
{
int count = 7;
resetValue(&count);
printf("After reset: %d\n", count);
return 0;
}
Output:
After reset: 0
The call passes &count, the address of count. The pointer parameter value receives a copy of that address. Inside the function, *value = 0; writes to the int stored at that address, so the variable in main changes.
Passing arrays with a length parameter
#include <stdio.h>
int sumArray(const int values[], int length)
{
int total = 0;
for (int i = 0; i < length; i++) {
total += values[i];
}
return total;
}
int main(void)
{
int scores[] = { 80, 90, 75, 85 };
int count = 4;
printf("Total: %d\n", sumArray(scores, count));
return 0;
}
Output:
Total: 330
The parameter values acts like a pointer to the first array element. The function also receives length because the array parameter does not carry size information. The const keyword tells readers and the compiler that sumArray should not modify the array elements.
How It Works Step By Step
Consider the call calculateTotal(1.25, 4). First, the caller evaluates both arguments. The floating-point constant 1.25 is used to initialize the parameter price, and 4 initializes quantity. The function call gets its own execution context, commonly described as a stack frame, containing those parameter values and any local variables created inside the function.
When return price * quantity; runs, the expression is evaluated using the parameter copies. The result is delivered back to the caller, and the function’s local context is discarded. This is why parameters are a clean way to pass data into a function without giving the function permanent ownership of the caller’s variables.
For pointer parameters, the same pass-by-value rule applies, but the copied value is an address. If main passes &count, the function receives a pointer value that identifies where count lives. Changing the pointer variable itself would not change count, but dereferencing it with *value accesses the object at that address.
For array parameters, C adjusts the parameter type. A declaration such as int values[] in a function parameter list means the function receives an int *. That is why sizeof values inside the function would measure the pointer, not the original array. Pass the length, use a sentinel value, or design another clear way for the function to know where the data ends.
Common Mistakes
Expecting a normal parameter to update the original variable
A common mistake is to write a function that assigns to an int parameter and expect the caller’s variable to change. The corrected version passes an address and uses a pointer parameter:
#include <stdio.h>
void addBonus(int *score, int bonus)
{
*score += bonus;
}
int main(void)
{
int score = 70;
addBonus(&score, 5);
printf("Score: %d\n", score);
return 0;
}
Output:
Score: 75
The first parameter is int *score, so the caller must pass &score. The expression *score += bonus; modifies the caller’s object instead of a temporary copy.
Omitting the array length
An array parameter does not know how many elements were in the original array. Do not try to compute the array’s element count from the parameter. Pass the count separately and loop only over that range:
#include <stdio.h>
void printTemperatures(const int temperatures[], int length)
{
for (int i = 0; i < length; i++) {
printf("Day %d: %d\n", i + 1, temperatures[i]);
}
}
int main(void)
{
int week[] = { 68, 70, 69 };
printTemperatures(week, 3);
return 0;
}
Output:
Day 1: 68
Day 2: 70
Day 3: 69
The separate length parameter makes the function’s boundary explicit. This prevents reading past the end of the array, which would produce undefined behavior.
Forgetting that each parameter needs a type
In C, int width, height is valid because both declarators are introduced by int, but a function parameter list cannot be written as void draw(width, height) in modern style. Use a type for every parameter:
#include <stdio.h>
void printBoxInfo(int width, int height)
{
printf("Box: %d by %d\n", width, height);
}
int main(void)
{
printBoxInfo(8, 3);
return 0;
}
Output:
Box: 8 by 3
The function header is also documentation. Anyone reading void printBoxInfo(int width, int height) can immediately see the number, order, and types of required arguments.
Best Practices
- Use clear parameter names that describe the role of each value, such as
width,height,length, orindex. - Keep parameter order predictable. For example, use
width, heightconsistently instead of switching order between functions. - Use
voidfor functions that accept no arguments, such asint readChoice(void). - Prefer return values for a single calculated result, and use pointer parameters when the function must modify caller data or produce multiple outputs.
- Use
conston pointer or array parameters when the function should read data but not modify it. - Pass array lengths explicitly unless your data format has a reliable terminator, such as the
'\0'character at the end of a C string. - Avoid very long parameter lists. If a function needs many related values, it may be time to introduce a
structin later lessons. - Compile with warnings enabled, such as
-Wall -Wextra, to catch mismatched parameter types, missing prototypes, and suspicious conversions.
Practice Exercises
- Write a function named
printInvoiceLinethat accepts a product code, quantity, and unit price, then prints one formatted line. - Write a function named
swapIntsthat accepts twoint *parameters and swaps the two caller variables. - Write a function named
averageArraythat accepts aconst intarray and a length, then returns the average as adouble.
Summary
- Parameters are local variables that receive argument values when a function is called.
- C passes arguments by value, so ordinary parameters receive copies.
- Changing a normal parameter does not change the caller’s variable.
- Pointer parameters let a function access or modify caller-owned objects through addresses.
- Array parameters are adjusted to pointers, so pass the array length separately.
- Prototypes and definitions must agree so the compiler can check calls correctly.
- Good parameter design makes functions easier to read, test, and reuse.
