Pointers in C exercises — master memory
Pointers in C exercises are where the mental model becomes instinct. You’ve seen the theory and built four complete programs. Now it’s time to solve challenges on your own in Fedora — copying arrays with pointers, counting and replacing elements, and a final challenge that implements a basic dynamic data structure.
Set up your workspace:
cd ~/GCID/IC2/Labs mkdir exercises_pointers cd exercises_pointers
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.
Pointers in C exercises — Basic Level
Exercise 1 — Copy and compare arrays
Write a C program with three functions that work with integer arrays using pointers:
copy_array(int *source, int *dest, int n)— copies n elements from source to destarrays_equal(int *a, int *b, int n)— returns 1 if all elements are equal, 0 otherwisefill_array(int *arr, int n, int value)— fills all n elements with value
=== ARRAY COPY AND COMPARE === Original array: 10 20 30 40 50 After copy: 10 20 30 40 50 Arrays equal?: Yes After fill with 7: 7 7 7 7 7 Equal to original?: No Copy of original: 10 20 30 40 50 Still equal?: Yes
💡 Hints:
copy_array:for (int i = 0; i < n; i++) *(dest+i) = *(source+i);arrays_equal: loop comparing*(a+i)with*(b+i)— return 0 at first mismatchfill_array: loop setting*(arr+i) = value- Print arrays with a separate function that takes
int *arr, int n
Exercise 2 — Count and replace
Write a C program with these pointer-based functions for integer arrays:
count_value(int *arr, int n, int target)— returns how many times target appearscount_greater(int *arr, int n, int threshold)— returns how many elements are greater than thresholdreplace_value(int *arr, int n, int old_val, int new_val)— replaces all occurrences of old_val with new_val, returns count of replacementsclamp_array(int *arr, int n, int min, int max)— sets any element below min to min and above max to max
=== COUNT AND REPLACE === Array: 3 7 2 7 5 7 1 8 7 4 Count of 7: 4 Count above 5: 3 After replace 7→0: 3 0 2 0 5 0 1 8 0 4 (4 replacements) After clamp [3,6]: 3 3 3 3 5 3 3 6 3 4
💡 Hints:
- All functions take
int *arr— walk with pointer arithmetic or array indexing replace_valueincrements a counter each time it replaces and returns that countclamp_arrayuses nestedif: if element < min set to min, else if element > max set to max
Pointers in C exercises — Intermediate Level
Exercise 3 — String operations without string.h
Implement these string functions using only char pointers — without using any function from <string.h>:
my_strlen(const char *s)— returns string length (without counting\0)my_strcpy(char *dest, const char *src)— copies src into destmy_strcat(char *dest, const char *src)— appends src to end of destmy_strcmp(const char *a, const char *b)— returns 0 if equal, negative if a < b, positive if a > bmy_contains(const char *haystack, char needle)— returns 1 if needle is in haystack
=== STRING FUNCTIONS === String 1: "Hello" String 2: " World" Length of s1: 5 Copy s2 to s3: " World" Concat s1+s2: "Hello World" Compare s1,s1: 0 (equal) Compare s1,s2: positive (H > space in ASCII) Contains 'o': Yes Contains 'z': No
💡 Hints:
my_strlen: walkpuntil*p == '\0', returnp - smy_strcpy:while ((*dest++ = *src++) != '\0');— copy and advance simultaneouslymy_strcat: first advance dest to its end, then copy srcmy_strcmp: walk both pointers, return*a - *bat first difference or when one ends
Pointers in C exercises — Final Challenge
Exercise 4 — Integer stack with pointers
Implement a basic stack (LIFO — Last In First Out) using an integer array and a pointer to track the top. The stack must support push, pop, peek and display.
=== INTEGER STACK === Push 10 → Stack: 10 Push 20 → Stack: 10 20 Push 30 → Stack: 10 20 30 Push 40 → Stack: 10 20 30 40 Peek (top): 40 Size: 4 Pop → 40 removed. Stack: 10 20 30 Pop → 30 removed. Stack: 10 20 Pop → 20 removed. Stack: 10 Pop → 10 removed. Stack: (empty) Pop on empty stack → Error: stack underflow
💡 Hints:
- Use
int data[MAX]for storage andint *toppointing to current top position push: check if full, then*top = value; top++;pop: check if empty, thentop--; return *top;(or use a temp)peek: return*(top-1)without moving topis_empty:top == data(pointer equals start of array)is_full:top == data + MAX- Pass the stack components as parameters:
int *data, int **top, int max
Commented solutions
Solution Exercise 1
#include <stdio.h>
void print_array(int *arr, int n) {
for (int i = 0; i < n; i++)
printf("%d ", *(arr+i));
printf("\n");
}
void copy_array(int *source, int *dest, int n) {
for (int i = 0; i < n; i++)
*(dest+i) = *(source+i);
}
int arrays_equal(int *a, int *b, int n) {
for (int i = 0; i < n; i++)
if (*(a+i) != *(b+i)) return 0;
return 1;
}
void fill_array(int *arr, int n, int value) {
for (int i = 0; i < n; i++)
*(arr+i) = value;
}
int main() {
int original[5] = {10, 20, 30, 40, 50};
int copy[5];
int n = 5;
printf("=== ARRAY COPY AND COMPARE ===\n\n");
printf("Original array: ");
print_array(original, n);
copy_array(original, copy, n);
printf("After copy: ");
print_array(copy, n);
printf("Arrays equal?: %s\n\n",
arrays_equal(original, copy, n) ? "Yes" : "No");
fill_array(copy, n, 7);
printf("After fill with 7: ");
print_array(copy, n);
printf("Equal to original?: %s\n\n",
arrays_equal(original, copy, n) ? "Yes" : "No");
copy_array(original, copy, n);
printf("Copy of original: ");
print_array(copy, n);
printf("Still equal?: %s\n",
arrays_equal(original, copy, n) ? "Yes" : "No");
return 0;
}
Solution Exercise 2
#include <stdio.h>
void print_array(int *arr, int n) {
for (int i = 0; i < n; i++) printf("%d ", *(arr+i));
printf("\n");
}
int count_value(int *arr, int n, int target) {
int count = 0;
for (int i = 0; i < n; i++)
if (*(arr+i) == target) count++;
return count;
}
int count_greater(int *arr, int n, int threshold) {
int count = 0;
for (int i = 0; i < n; i++)
if (*(arr+i) > threshold) count++;
return count;
}
int replace_value(int *arr, int n, int old_val, int new_val) {
int count = 0;
for (int i = 0; i < n; i++) {
if (*(arr+i) == old_val) {
*(arr+i) = new_val;
count++;
}
}
return count;
}
void clamp_array(int *arr, int n, int min, int max) {
for (int i = 0; i < n; i++) {
if (*(arr+i) < min) *(arr+i) = min;
else if (*(arr+i) > max) *(arr+i) = max;
}
}
int main() {
int arr[10] = {3, 7, 2, 7, 5, 7, 1, 8, 7, 4};
int n = 10;
printf("=== COUNT AND REPLACE ===\n\n");
printf("Array: ");
print_array(arr, n);
printf("Count of 7: %d\n", count_value(arr, n, 7));
printf("Count above 5: %d\n", count_greater(arr, n, 5));
int replaced = replace_value(arr, n, 7, 0);
printf("After replace 7→0: ");
print_array(arr, n);
printf("(%d replacements)\n", replaced);
clamp_array(arr, n, 3, 6);
printf("After clamp [3,6]: ");
print_array(arr, n);
return 0;
}
Solution Exercise 3
#include <stdio.h>
int my_strlen(const char *s) {
const char *p = s;
while (*p != '\0') p++;
return (int)(p - s);
}
void my_strcpy(char *dest, const char *src) {
while ((*dest++ = *src++) != '\0');
}
void my_strcat(char *dest, const char *src) {
while (*dest != '\0') dest++; /* advance to end of dest */
while ((*dest++ = *src++) != '\0'); /* copy src */
}
int my_strcmp(const char *a, const char *b) {
while (*a != '\0' && *b != '\0' && *a == *b) {
a++;
b++;
}
return (unsigned char)*a - (unsigned char)*b;
}
int my_contains(const char *haystack, char needle) {
while (*haystack != '\0') {
if (*haystack == needle) return 1;
haystack++;
}
return 0;
}
int main() {
printf("=== STRING FUNCTIONS ===\n\n");
char s1[50] = "Hello";
char s2[50] = " World";
char s3[50];
char s4[100] = "Hello";
printf("String 1: \"%s\"\n", s1);
printf("String 2: \"%s\"\n\n", s2);
printf("Length of s1: %d\n", my_strlen(s1));
my_strcpy(s3, s2);
printf("Copy s2 to s3: \"%s\"\n", s3);
my_strcat(s4, s2);
printf("Concat s1+s2: \"%s\"\n", s4);
printf("Compare s1,s1: %d (%s)\n",
my_strcmp(s1, s1),
my_strcmp(s1, s1) == 0 ? "equal" : "not equal");
int cmp = my_strcmp(s1, s2);
printf("Compare s1,s2: %s\n",
cmp > 0 ? "positive (H > space in ASCII)" :
cmp < 0 ? "negative" : "equal");
printf("Contains 'o': %s\n",
my_contains(s1, 'o') ? "Yes" : "No");
printf("Contains 'z': %s\n",
my_contains(s1, 'z') ? "Yes" : "No");
return 0;
}
Solution Exercise 4
#include <stdio.h>
#define MAX 10
typedef struct {
int data[MAX];
int *top; /* points to next empty slot */
} Stack;
void stack_init(Stack *s) {
s->top = s->data; /* top points to start — stack is empty */
}
int stack_is_empty(Stack *s) {
return s->top == s->data;
}
int stack_is_full(Stack *s) {
return s->top == s->data + MAX;
}
int stack_size(Stack *s) {
return (int)(s->top - s->data);
}
int stack_push(Stack *s, int value) {
if (stack_is_full(s)) {
printf("Error: stack overflow\n");
return 0;
}
*s->top = value;
s->top++;
return 1;
}
int stack_pop(Stack *s, int *value) {
if (stack_is_empty(s)) {
printf("Error: stack underflow\n");
return 0;
}
s->top--;
*value = *s->top;
return 1;
}
int stack_peek(Stack *s, int *value) {
if (stack_is_empty(s)) {
printf("Error: stack is empty\n");
return 0;
}
*value = *(s->top - 1);
return 1;
}
void stack_print(Stack *s) {
if (stack_is_empty(s)) {
printf("(empty)");
return;
}
int *p = s->data;
while (p < s->top) {
printf("%d ", *p);
p++;
}
}
int main() {
Stack s;
stack_init(&s);
int value;
printf("=== INTEGER STACK ===\n\n");
int to_push[4] = {10, 20, 30, 40};
for (int i = 0; i < 4; i++) {
stack_push(&s, to_push[i]);
printf("Push %d → Stack: ", to_push[i]);
stack_print(&s);
printf("\n");
}
stack_peek(&s, &value);
printf("\nPeek (top): %d\n", value);
printf("Size: %d\n\n", stack_size(&s));
while (!stack_is_empty(&s)) {
stack_pop(&s, &value);
printf("Pop → %d removed. Stack: ", value);
stack_print(&s);
printf("\n");
}
/* Test underflow */
stack_pop(&s, &value);
return 0;
}
Visualise with Python Tutor
Select C from the dropdown and paste in pythontutor.com:
#include <stdio.h>
void copy_array(int *src, int *dst, int n) {
for (int i = 0; i < n; i++)
*(dst + i) = *(src + i);
}
int main() {
int a[3] = {10, 20, 30};
int b[3];
copy_array(a, b, 3);
for (int i = 0; i < 3; i++)
printf("b[%d] = %d\n", i, b[i]);
return 0;
}
Step through copy_array carefully. When the function is called, src receives the address of a[0] and dst receives the address of b[0] — two separate arrows in memory. On each iteration *(src + i) follows the src arrow, steps i positions forward, and reads the value there. *(dst + i) follows the dst arrow and writes to that position. After the loop both a and b contain the same values but in completely different memory locations — they are genuinely independent copies. Change b[0] after the copy and watch that a[0] doesn’t change — that independence is the whole point of copy functions.
Cheat sheet — Pointers in C
/* ============================================
CHEAT SHEET — Pointers in C
Sergio Learns · sergiolearns.com
============================================ */
/* DECLARING A POINTER */
int *p; /* pointer to int */
double *p; /* pointer to double */
char *p; /* pointer to char (and strings) */
/* & — ADDRESS-OF OPERATOR */
int x = 5;
int *p = &x; /* p stores the address of x */
printf("%p", &x); /* print address — use %p */
/* * — TWO USES */
int *p; /* in declaration: "p is a pointer to int" */
*p = 10; /* as operator: "value AT address p" */
printf("%d", *p); /* dereference: read value at address */
/* THE COMPLETE PICTURE */
int x = 5;
int *p = &x;
/* x → 5 (the value) */
/* &x → 0x.. (the address) */
/* p → 0x.. (p stores the same address as &x) */
/* *p → 5 (value at address p — same as x) */
/* MODIFYING THROUGH POINTER */
*p = 20; /* x is now 20 */
/* NULL — uninitialised pointer */
int *p = NULL;
if (p != NULL) { *p = 5; } /* always check before use */
/* *p = 5 when p is NULL → segfault crash */
/* POINTER ARITHMETIC */
int arr[5] = {10,20,30,40,50};
int *p = arr; /* p → arr[0] */
*(p+1) /* → 20 (arr[1]) */
*(p+i) /* → arr[i] */
p++ /* advance by sizeof(int) = 4 bytes */
p < arr + 5 /* check: not past end of array */
/* ARRAYS AND POINTERS — EQUIVALENT */
arr[i] == *(arr + i) /* same thing */
arr == &arr[0] /* array name = address of first element */
/* PASS BY POINTER — modify original */
void double_val(int *p) { *p *= 2; }
double_val(&x); /* x is modified */
/* ARRAYS TO FUNCTIONS — no & needed */
void process(int *arr, int n) { ... }
process(numbers, n); /* array name already is a pointer */
/* MULTIPLE RETURN VALUES */
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 */
/* STRINGS AS CHAR POINTERS */
char s[] = "Hello";
char *p = s;
while (*p != '\0') { /* walk until null terminator */
printf("%c", *p);
p++;
}
/* POINTER SUBTRACTION */
char *start = s;
char *end = s;
while (*end != '\0') end++;
int length = end - start; /* number of elements between */
/* TWO-POINTER TECHNIQUE */
int *left = arr;
int *right = arr + n - 1;
while (left < right) {
/* swap *left and *right */
int tmp = *left; *left = *right; *right = tmp;
left++; right--;
}
/* CONST POINTER — read-only parameter */
void print_str(const char *s) {
/* *s = 'X'; → compile error — const prevents modification */
printf("%s", s);
}
/* COMMON ERRORS */
/* 1. Dereferencing NULL → segfault */
/* 2. Using uninitialised pointer */
/* 3. Forgetting & in function call: func(x) not func(&x) */
/* 4. Confusing p (address) with *p (value) */
/* 5. p++ moves sizeof(type) bytes, not 1 byte for int/double */
/* COMPILE AND RUN */
/* gcc exercises.c -o exercises -Wall */
/* ./exercises */
