Functions in C exercises — master pass by reference
Functions in C exercises are where pass by value and pass by reference stop being abstract concepts and become tools you apply automatically. You’ve seen the theory and built three complete programs. Now it’s time to solve challenges on your own — finding the second maximum, deleting an element by position, and merging two sorted arrays.
Set up your workspace:
cd ~/GCID/IC2/Labs mkdir exercises_functions cd exercises_functions
As always: try to solve it in gedit, compile with gcc, use the hint if stuck for more than 10 minutes, and compare with the commented solution.
Table of Contents
Functions in c exercises — Basic Level
Exercise 1 — Second maximum
Write a C program with these functions for integer arrays:
find_second_max(arr, n, *second)— finds the second largest distinct value. Returns 1 if found (at least 2 distinct values exist), 0 otherwise. Returns the value through the pointer.find_top_k(arr, n, k, result)— fillsresultwith the top k distinct values in descending order. Returns the number of distinct values actually found (may be less than k).count_distinct(arr, n)— returns how many distinct values exist in the array.
Array: [3, 1, 4, 1, 5, 9, 2, 6, 5, 3] Distinct values: 7 Maximum: 9 Second maximum: 6 Top 3: [9, 6, 5] Top 5: [9, 6, 5, 4, 3] Top 10: [9, 6, 5, 4, 3, 2, 1] (only 7 distinct values)
💡 Hints:
find_second_max: traverse once trackingmaxandsecond_max. Whenarr[i] > max:second_max = max; max = arr[i]. Whenarr[i] > second_max && arr[i] != max:second_max = arr[i].count_distinct: for each element, check if it appeared earlier in the array. If not, count it.find_top_k: sort a copy of the array descending, then collect distinct values until you have k or run out.- Pass
secondasint *secondand write*second = valueinside the function.
Exercise 2 — Delete element by position
Write a C program with these functions:
delete_at(arr, *n, index)— removes the element atindex, shifts all subsequent elements left, decrements*n. Returns 1 if successful, 0 if index out of range.delete_value(arr, *n, value)— removes the first occurrence ofvalue. Returns 1 if found and removed, 0 if not found.delete_all(arr, *n, value)— removes ALL occurrences ofvalue. Returns the number of deletions.insert_at(arr, *n, index, value, max_size)— insertsvalueatindex, shifts elements right. Returns 1 if successful, 0 if array is full or index out of range.
Array: [1, 2, 3, 4, 5, 3, 6, 3] n=8 delete_at(arr, &n, 2): [1, 2, 4, 5, 3, 6, 3] n=7 delete_value(arr, &n, 3): [1, 2, 4, 5, 3, 6, 3] → removes first 3 delete_all(arr, &n, 3): removes all 3s insert_at(arr, &n, 1, 99, MAX): inserts 99 at position 1
💡 Hints:
delete_at: shift left:for (i = index; i < n-1; i++) arr[i] = arr[i+1]; (*n)--;delete_value: find the index of the first occurrence, then calldelete_atdelete_all: walk with two indices —readandwrite. Copy elements that don’t match, skip those that do. Update*nat the end.insert_at: first check bounds and*n < max_size. Shift right:for (i = *n; i > index; i--) arr[i] = arr[i-1]. Then setarr[index] = value; (*n)++;*nis a pointer to int — you need(*n)--not*n--(operator precedence)
Functions in c exercises — Intermediate Level → Final Challenge
Exercise 3 — Merge two sorted arrays
Write a C program that merges two sorted integer arrays into a single sorted array — without using any sorting function on the result. The merge must be done in O(n+m) time using the two-pointer technique.
Also implement:
intersection(a, na, b, nb, result, *nr)— elements that appear in both arraysunion_arrays(a, na, b, nb, result, *nr)— all distinct elements from both arraysdifference(a, na, b, nb, result, *nr)— elements inathat are not inb
All input arrays are sorted. All result arrays must also be sorted.
A = [1, 3, 5, 7, 9] (sorted) B = [2, 3, 4, 7, 8, 10] (sorted) Merge: [1, 2, 3, 3, 4, 5, 7, 7, 8, 9, 10] Intersection: [3, 7] Union: [1, 2, 3, 4, 5, 7, 8, 9, 10] Difference A-B: [1, 5, 9]
💡 Hints:
- Merge: use two indices
i=0, j=0. While both in range: take the smaller and advance its index. When one runs out, copy the rest of the other. - Intersection: advance through both. When
a[i] == b[j], add to result and advance both. Whena[i] < b[j], advance i. Otherwise advance j. - Union: similar to merge but skip duplicates — when both have the same value, add once and advance both.
- Difference: advance through
a. For eacha[i], binary search inbto check if it exists. If not found, add to result.
Commented solutions
Solution Exercise 1
#include <stdio.h>
#define MAX 100
void print_array(const int *arr, int n) {
printf("[");
for (int i = 0; i < n; i++) {
printf("%d", arr[i]);
if (i < n - 1) printf(", ");
}
printf("]");
}
int count_distinct(const int *arr, int n) {
int count = 0;
for (int i = 0; i < n; i++) {
int already_seen = 0;
for (int j = 0; j < i; j++) {
if (arr[j] == arr[i]) {
already_seen = 1;
break;
}
}
if (!already_seen) count++;
}
return count;
}
int find_max(const int *arr, int n) {
int max = arr[0];
for (int i = 1; i < n; i++)
if (arr[i] > max) max = arr[i];
return max;
}
int find_second_max(const int *arr, int n, int *second) {
if (count_distinct(arr, n) < 2)
return 0; /* need at least 2 distinct values */
int max = find_max(arr, n);
int sec = -2147483648; /* INT_MIN */
int found = 0;
for (int i = 0; i < n; i++) {
if (arr[i] != max && arr[i] > sec) {
sec = arr[i];
found = 1;
}
}
if (found) *second = sec;
return found;
}
/* Sort a copy descending and collect distinct values */
int find_top_k(const int *arr, int n, int k, int *result) {
/* Make a sorted copy */
int copy[MAX];
for (int i = 0; i < n; i++) copy[i] = arr[i];
/* Bubble sort descending */
for (int i = 0; i < n - 1; i++)
for (int j = 0; j < n - 1 - i; j++)
if (copy[j] < copy[j+1]) {
int tmp = copy[j];
copy[j] = copy[j+1];
copy[j+1] = tmp;
}
/* Collect k distinct values */
int count = 0;
for (int i = 0; i < n && count < k; i++) {
if (i == 0 || copy[i] != copy[i-1]) {
result[count++] = copy[i];
}
}
return count;
}
int main() {
int arr[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
int n = 10;
printf("Array: ");
print_array(arr, n);
printf("\n\n");
printf("Distinct values: %d\n", count_distinct(arr, n));
printf("Maximum: %d\n", find_max(arr, n));
int second;
if (find_second_max(arr, n, &second))
printf("Second maximum: %d\n", second);
else
printf("No second maximum (all values equal)\n");
printf("\n");
int top[MAX], found;
int k_values[] = {3, 5, 10};
for (int t = 0; t < 3; t++) {
int k = k_values[t];
found = find_top_k(arr, n, k, top);
printf("Top %2d: ", k);
print_array(top, found);
if (found < k)
printf(" (only %d distinct values)", found);
printf("\n");
}
/* Edge cases */
int all_same[] = {5, 5, 5};
printf("\nAll same [5,5,5]: second_max = ");
if (find_second_max(all_same, 3, &second))
printf("%d\n", second);
else
printf("none\n");
return 0;
}
Solution Exercise 2
#include <stdio.h>
#define MAX 50
void print_array(const int *arr, int n) {
printf("[");
for (int i = 0; i < n; i++) {
printf("%d", arr[i]);
if (i < n - 1) printf(", ");
}
printf("] n=%d\n", n);
}
int delete_at(int *arr, int *n, int index) {
if (index < 0 || index >= *n)
return 0; /* out of range */
for (int i = index; i < *n - 1; i++)
arr[i] = arr[i+1]; /* shift left */
(*n)--;
return 1;
}
int delete_value(int *arr, int *n, int value) {
for (int i = 0; i < *n; i++) {
if (arr[i] == value)
return delete_at(arr, n, i);
}
return 0; /* not found */
}
int delete_all(int *arr, int *n, int value) {
int write = 0, removed = 0;
for (int read = 0; read < *n; read++) {
if (arr[read] != value) {
arr[write++] = arr[read];
} else {
removed++;
}
}
*n = write;
return removed;
}
int insert_at(int *arr, int *n, int index, int value, int max_size) {
if (*n >= max_size || index < 0 || index > *n)
return 0; /* full or out of range */
for (int i = *n; i > index; i--)
arr[i] = arr[i-1]; /* shift right */
arr[index] = value;
(*n)++;
return 1;
}
int main() {
int arr[MAX] = {1, 2, 3, 4, 5, 3, 6, 3};
int n = 8;
printf("Original: ");
print_array(arr, n);
/* delete_at */
int arr2[MAX];
for (int i = 0; i < n; i++) arr2[i] = arr[i];
int n2 = n;
delete_at(arr2, &n2, 2);
printf("delete_at(2): ");
print_array(arr2, n2);
/* delete_value */
int arr3[MAX];
for (int i = 0; i < n; i++) arr3[i] = arr[i];
int n3 = n;
int found = delete_value(arr3, &n3, 3);
printf("delete_value(3): ");
print_array(arr3, n3);
printf(" → removed: %s\n", found ? "yes" : "no");
/* delete_all */
int arr4[MAX];
for (int i = 0; i < n; i++) arr4[i] = arr[i];
int n4 = n;
int removed = delete_all(arr4, &n4, 3);
printf("delete_all(3): ");
print_array(arr4, n4);
printf(" → removed %d occurrence(s)\n", removed);
/* insert_at */
int arr5[MAX];
for (int i = 0; i < n4; i++) arr5[i] = arr4[i];
int n5 = n4;
insert_at(arr5, &n5, 1, 99, MAX);
printf("insert_at(1, 99): ");
print_array(arr5, n5);
/* Edge cases */
printf("\n--- Edge cases ---\n");
int small[] = {10, 20, 30};
int ns = 3;
printf("delete_at(-1) returns: %d\n", delete_at(small, &ns, -1));
printf("delete_at(3) returns: %d\n", delete_at(small, &ns, 3));
printf("delete_value(99) returns: %d\n", delete_value(small, &ns, 99));
return 0;
}
Solution Exercise 3
#include <stdio.h>
#define MAX 200
void print_array(const int *arr, int n, const char *label) {
printf("%s[", label);
for (int i = 0; i < n; i++) {
printf("%d", arr[i]);
if (i < n - 1) printf(", ");
}
printf("] (n=%d)\n", n);
}
/* Merge two sorted arrays — O(n+m) */
void merge_sorted(const int *a, int na,
const int *b, int nb,
int *result, int *nr) {
int i = 0, j = 0, k = 0;
while (i < na && j < nb) {
if (a[i] <= b[j])
result[k++] = a[i++];
else
result[k++] = b[j++];
}
/* Copy remaining elements */
while (i < na) result[k++] = a[i++];
while (j < nb) result[k++] = b[j++];
*nr = k;
}
/* Elements in both A and B */
void intersection(const int *a, int na,
const int *b, int nb,
int *result, int *nr) {
int i = 0, j = 0, k = 0;
int prev = -2147483648; /* track duplicates in result */
while (i < na && j < nb) {
if (a[i] == b[j]) {
if (a[i] != prev) { /* avoid duplicates in result */
result[k++] = a[i];
prev = a[i];
}
i++;
j++;
} else if (a[i] < b[j]) {
i++;
} else {
j++;
}
}
*nr = k;
}
/* All distinct elements from A and B */
void union_arrays(const int *a, int na,
const int *b, int nb,
int *result, int *nr) {
int i = 0, j = 0, k = 0;
int prev = -2147483648;
while (i < na && j < nb) {
int val;
if (a[i] < b[j]) val = a[i++];
else if (b[j] < a[i]) val = b[j++];
else { val = a[i++]; j++; } /* same — take once, advance both */
if (val != prev) {
result[k++] = val;
prev = val;
}
}
while (i < na) {
if (a[i] != prev) { result[k++] = a[i]; prev = a[i]; }
i++;
}
while (j < nb) {
if (b[j] != prev) { result[k++] = b[j]; prev = b[j]; }
j++;
}
*nr = k;
}
/* Binary search — returns 1 if found, 0 otherwise */
int binary_search(const int *arr, int n, int target) {
int lo = 0, hi = n - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (arr[mid] == target) return 1;
if (arr[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
return 0;
}
/* Elements in A that are not in B */
void difference(const int *a, int na,
const int *b, int nb,
int *result, int *nr) {
int k = 0;
int prev = -2147483648;
for (int i = 0; i < na; i++) {
if (a[i] != prev && !binary_search(b, nb, a[i])) {
result[k++] = a[i];
prev = a[i];
}
}
*nr = k;
}
int main() {
int a[] = {1, 3, 5, 7, 9};
int b[] = {2, 3, 4, 7, 8, 10};
int na = 5, nb = 6;
printf("=== SORTED ARRAY OPERATIONS ===\n\n");
print_array(a, na, "A = ");
print_array(b, nb, "B = ");
printf("\n");
int result[MAX];
int nr;
merge_sorted(a, na, b, nb, result, &nr);
print_array(result, nr, "Merge: ");
intersection(a, na, b, nb, result, &nr);
print_array(result, nr, "Intersection: ");
union_arrays(a, na, b, nb, result, &nr);
print_array(result, nr, "Union: ");
difference(a, na, b, nb, result, &nr);
print_array(result, nr, "Difference A-B: ");
difference(b, nb, a, na, result, &nr);
print_array(result, nr, "Difference B-A: ");
/* Edge cases */
printf("\n--- Edge cases ---\n");
int empty[] = {};
merge_sorted(a, na, empty, 0, result, &nr);
print_array(result, nr, "Merge A+empty: ");
int identical[] = {1, 3, 5, 7, 9};
intersection(a, na, identical, 5, result, &nr);
print_array(result, nr, "Intersection identical: ");
return 0;
}
Visualise with Python Tutor
Select C from the dropdown and paste in pythontutor.com:
#include <stdio.h>
int delete_at(int *arr, int *n, int index) {
if (index < 0 || index >= *n) return 0;
for (int i = index; i < *n - 1; i++)
arr[i] = arr[i+1];
(*n)--;
return 1;
}
int main() {
int arr[] = {10, 20, 30, 40, 50};
int n = 5;
printf("Before: ");
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
printf("n=%d\n", n);
delete_at(arr, &n, 1); /* delete element at index 1 (value 20) */
printf("After: ");
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
printf("n=%d\n", n);
return 0;
}
Step through delete_at carefully. When called, arr receives the address of arr[0] in main — an arrow pointing into main’s array. n receives the address of main’s n variable — another arrow. Inside the function, *n reads through that arrow: it sees 5. The shift loop arr[i] = arr[i+1] follows the arr arrow and modifies main’s array directly — you can see the values shifting left in Python Tutor’s memory diagram. Then (*n)-- — note the parentheses — dereferences n first (getting 5), then decrements what’s stored at that address (now 4). Without parentheses *n-- would decrement the pointer itself, not the value — a subtle but critical difference. After the function returns, main’s arr has shifted elements and main’s n is 4.
Cheat sheet — Functions in C
/* ============================================
CHEAT SHEET — Functions in C
Sergio Learns · sergiolearns.com
============================================ */
/* FUNCTION STRUCTURE */
return_type name(type param1, type param2) {
/* body */
return value; /* void functions use bare return; */
}
/* PROTOTYPE — declare before calling */
int add(int a, int b); /* full declaration */
int add(int, int); /* parameter names optional */
/* VOID — no return value */
void print_info(int x) {
printf("%d\n", x);
/* optional: return; to exit early */
}
/* PASS BY VALUE — function gets a copy */
void f_value(int n) { n = 99; }
int x = 5;
f_value(x);
/* x is still 5 */
/* PASS BY REFERENCE — function gets the address */
void f_ref(int *p) { *p = 99; }
int x = 5;
f_ref(&x); /* & gives the address */
/* x is now 99 */
/* MULTIPLE RETURN VALUES — through pointer params */
void divide(int a, int b, int *q, int *r) {
*q = a / b;
*r = a % b;
}
int q, r;
divide(17, 5, &q, &r); /* q=3, r=2 */
/* ARRAYS — always passed by reference (pointer) */
void process(int *arr, int n) { arr[i] = ...; }
process(numbers, n); /* no & needed — arr name IS &arr[0] */
/* const — read-only parameter */
void print(const int *arr, int n) {
/* arr[i] = 0; → compile error */
}
/* DELETE ELEMENT — shift pattern */
void delete_at(int *arr, int *n, int idx) {
for (int i = idx; i < *n - 1; i++)
arr[i] = arr[i+1]; /* shift left */
(*n)--; /* (*n)-- not *n-- */
}
/* INSERT ELEMENT — shift pattern */
void insert_at(int *arr, int *n, int idx, int val) {
for (int i = *n; i > idx; i--)
arr[i] = arr[i-1]; /* shift right */
arr[idx] = val;
(*n)++;
}
/* MERGE TWO SORTED ARRAYS — two-pointer O(n+m) */
void merge(int *a, int na, int *b, int nb,
int *result, int *nr) {
int i = 0, j = 0, k = 0;
while (i < na && j < nb)
result[k++] = (a[i] <= b[j]) ? a[i++] : b[j++];
while (i < na) result[k++] = a[i++];
while (j < nb) result[k++] = b[j++];
*nr = k;
}
/* FIND SECOND MAX — single pass */
void second_max(int *arr, int n, int *max, int *sec) {
*max = *sec = -2147483648; /* INT_MIN */
for (int i = 0; i < n; i++) {
if (arr[i] > *max) { *sec = *max; *max = arr[i]; }
else if (arr[i] > *sec && arr[i] != *max)
*sec = arr[i];
}
}
/* COMMON MISTAKES */
/* 1. Forgetting & in call: f(x) vs f(&x) */
/* 2. (*n)-- vs *n-- (precedence: *n-- decrements pointer) */
/* 3. Calling without prototype before definition */
/* 4. Returning local array — it disappears after return */
/* 5. Not checking bounds before delete/insert */
/* 6. Modifying read-only const parameter */
/* COMPILE AND RUN */
/* gcc exercise.c -o exercise -Wall */
/* ./exercise */

One Comment