In C, functions receive inputs through parameters. The values passed by the caller are actual arguments (also called actual parameters), while the variables listed in the function definition are formal parameters. Understanding their distinction is crucial for avoiding bugs related to argument modifications.
Formal vs. Actual Parameters
An actual argument is the expression supplied in a function call. It could be a constant, a variable, an arithmetic expression, or the result of another function. It must evaluate to a definite value when the function is invoked. A formal parameter is the variable declared in the function header. It comes into existence (has memory allocated) only when the function is entered, and is destroyed upon returning. Changes to the formal parameter do not affect the actual argument unless the parameter is a pointer that is dereferenced.
Call Mechanisms
Pass‑by‑Value When arguments are passed by value, the formal parameters receive copies of the actual values. Modifying the formal parameters inside the function has no effect on the original variables.
Pass‑by‑Address Passing the adress of a variable gives the function direct access to the original memory location. By dereferencing the pointer parameter, the function can modify the caller’s variable.
These two mechanisms are illustrated in the following examples.
Example 1: Swapping Values
Attempting to swap two integer variables using pass‑by‑value fails because the function works on local copies. Using pointers succeeds.
#include <stdio.h>
void bad_swap(int p, int q) {
int tmp = p;
p = q;
q = tmp;
// p, q swapped locally, no effect on callers
}
void swap(int *a, int *b) {
int hold = *a;
*a = *b;
*b = hold;
}
int main() {
int m = 5, n = 15;
bad_swap(m, n);
printf("After bad_swap: m = %d, n = %d\n", m, n);
int x = 3, y = 7;
swap(&x, &y);
printf("After swap: x = %d, y = %d\n", x, y);
return 0;
}
Example 2: Prime Number Detector
A function is_prime_number returns 1 if the argument is prime, otherwise 0. It tests divisors up to the square root of n.
#include <stdio.h>
#include <math.h>
int is_prime_number(int n) {
if (n < 2) return 0;
for (int divisor = 2; divisor <= sqrt(n); divisor++) {
if (n % divisor == 0) return 0;
}
return 1;
}
int main() {
printf("Primes between 200 and 250:\n");
for (int num = 200; num <= 250; num++) {
if (is_prime_number(num)) {
printf("%d ", num);
}
}
return 0;
}
Example 3: Identifying Leap Years
A function is_leap takes a year and returns 1 if it is a leap year (divisible by 4 but not by 100, or divisible by 400).
#include <stdio.h>
int is_leap(int yr) {
return (yr % 4 == 0 && yr % 100 != 0) || (yr % 400 == 0);
}
int main() {
printf("Leap years from 1500 to 1600:\n");
for (int y = 1500; y <= 1600; y++) {
if (is_leap(y)) {
printf("%5d", y);
}
}
return 0;
}
Example 4: Binary Search on a Sorted Array
The function binary_search returns the index of a key in a sorted array of integers, or -1 if not found. The array is passed as a pointer to its first element.
#include <stdio.h>
int binary_search(const int data[], int key, int size) {
int low = 0, high = size - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (data[mid] < key)
low = mid + 1;
else if (data[mid] > key)
high = mid - 1;
else
return mid;
}
return -1;
}
int main() {
int arr[] = {5, 10, 15, 20, 25, 30, 35, 40};
int size = sizeof(arr) / sizeof(arr[0]);
int target = 25;
int index = binary_search(arr, target, size);
if (index != -1)
printf("Found %d at index %d\n", target, index);
else
printf("%d not found.\n", target);
return 0;
}
Example 5: Incrementing Through Pointers
A helper function increment takes a pointer to an integer and adds 1 to the value it points to. Each call to this function changes the caller’s variable.
#include <stdio.h>
void increment(int *p) {
(*p)++;
}
int main() {
int counter = 10;
printf("Initial: %d\n", counter);
increment(&counter);
printf("After 1st call: %d\n", counter);
increment(&counter);
printf("After 2nd call: %d\n", counter);
increment(&counter);
printf("After 3rd call: %d\n", counter);
return 0;
}
The examples above demonstrate that pass‑by‑value createes copies independent of the original variables, while pass‑by‑address allows the function to manipulate external data. This behavior underlies many common programming patterns in C.