Programming Language & Its Applications — Pointers, Structure and Data Files in C, NEC licence examination syllabus (Nepal Engineering Council).
The only way a C function can modify its caller's variables — plus function pointers, which let you pass behaviour itself.
Because C passes everything by value, a function that must change the caller's data has to receive an address. That is the whole mechanism behind scanf("%d", &n), behind every in-place sort, and behind returning multiple results. This topic also covers the reverse idea — a pointer to a function, which is how callbacks and qsort work.
onclick handler is a function pointer. A signal handler installed with signal(SIGINT, handler) is a function pointer. Every plugin system and every C++ virtual function is built on this — a vtable is nothing but an array of function pointers, exactly like the dispatch table below. Search "how C++ virtual functions are implemented" and you will find this code with different syntax.#include <stdio.h>
void increment(int *p) { (*p)++; }
void set_zero(int *p) { *p = 0; }
void swap(int *a, int *b) { int t=*a; *a=*b; *b=t; }
/* returning several results through pointers */
void circle(double r, double *area, double *circum) {
const double PI = 3.14159265358979;
*area = PI * r * r;
*circum = 2 * PI * r;
}
int main(void) {
int n = 10;
increment(&n); printf("after increment: %d\n", n);
swap(&n, (int[]){99}); /* compound literal */
int x = 3, y = 7;
swap(&x, &y); printf("swapped: x=%d y=%d\n", x, y);
double a, c;
circle(2.5, &a, &c);
printf("r=2.5 area=%.4f circumference=%.4f\n", a, c);
return 0;
}
Output:
after increment: 11
swapped: x=7 y=3
r=2.5 area=19.6350 circumference=15.7080
#include <stdio.h>
void print(const char *tag, const int *a, int n) {
printf("%-10s", tag);
for (int i = 0; i < n; i++) printf("%3d", a[i]);
printf("\n");
}
void reverse(int *a, int n) {
for (int i = 0, j = n-1; i < j; i++, j--) {
int t = a[i]; a[i] = a[j]; a[j] = t;
}
}
void bubble_sort(int *a, int n) {
for (int i = 0; i < n-1; i++) {
int swapped = 0;
for (int j = 0; j < n-1-i; j++)
if (a[j] > a[j+1]) {
int t=a[j]; a[j]=a[j+1]; a[j+1]=t; swapped=1;
}
if (!swapped) break; /* already sorted */
}
}
void rotate_left(int *a, int n, int k) {
k %= n;
reverse(a, k); reverse(a+k, n-k); reverse(a, n);
}
int main(void) {
int a[] = {45, 12, 78, 3, 56, 91, 23};
int n = 7;
print("original", a, n);
reverse(a, n); print("reversed", a, n);
bubble_sort(a, n); print("sorted", a, n);
rotate_left(a, n, 3); print("rot 3", a, n);
return 0;
}
Output:
original 45 12 78 3 56 91 23
reversed 23 91 56 3 78 12 45
sorted 3 12 23 45 56 78 91
rot 3 45 56 78 91 3 12 23
#include <stdio.h>
#include <stdlib.h>
/* To change WHERE a caller's pointer points, you need
the address OF THE POINTER: a pointer to pointer. */
void allocate(int **pp, int n) {
*pp = malloc(n * sizeof **pp);
for (int i = 0; i < n; i++) (*pp)[i] = i * i;
}
/* the WRONG version - modifies a copy of the pointer */
void allocate_wrong(int *p, int n) {
p = malloc(n * sizeof *p); /* caller never sees this */
(void)p;
}
int main(void) {
int *arr = NULL;
allocate_wrong(arr, 5);
printf("after wrong: arr = %s\n", arr ? "set" : "still NULL");
allocate(&arr, 5);
printf("after right: ");
for (int i = 0; i < 5; i++) printf("%d ", arr[i]);
printf("\n");
free(arr);
/* three levels of indirection */
int v = 42, *p = &v, **q = &p, ***r = &q;
printf("v=%d *p=%d **q=%d ***r=%d\n", v, *p, **q, ***r);
return 0;
}
Output:
after wrong: arr = still NULL
after right: 0 1 4 9 16
v=42 *p=42 **q=42 ***r=42
The rule generalises: to modify something of type T, pass a T*. To modify an int, pass int*. To modify an int*, pass int**. That is why linked-list functions that can change the head take a Node** — an idea you will meet again in the DSA paper.
#include <stdio.h>
#include <stdlib.h>
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
/* a function taking a function - higher-order C */
void apply(const char *name, int (*op)(int,int),
int a, int b) {
printf("%-4s(%d,%d) = %d\n", name, a, b, op(a,b));
}
/* qsort needs a comparator matching this exact signature */
int cmp_asc(const void *x, const void *y) {
return (*(const int*)x) - (*(const int*)y);
}
int cmp_desc(const void *x, const void *y) {
return (*(const int*)y) - (*(const int*)x);
}
int main(void) {
apply("add", add, 12, 5);
apply("sub", sub, 12, 5);
apply("mul", mul, 12, 5);
/* dispatch table replaces a switch */
int (*ops[3])(int,int) = { add, sub, mul };
const char *nm[3] = { "+", "-", "*" };
for (int i = 0; i < 3; i++)
printf("7 %s 3 = %d\n", nm[i], ops[i](7,3));
int a[] = {45, 12, 78, 3, 56};
qsort(a, 5, sizeof a[0], cmp_asc);
printf("asc :"); for (int i=0;i<5;i++) printf(" %d",a[i]);
qsort(a, 5, sizeof a[0], cmp_desc);
printf("\ndesc:"); for (int i=0;i<5;i++) printf(" %d",a[i]);
printf("\n");
return 0;
}
Output:
add (12,5) = 17
sub (12,5) = 7
mul (12,5) = 60
7 + 3 = 10
7 - 3 = 4
7 * 3 = 21
asc : 3 12 45 56 78
desc: 78 56 45 12 3
The dispatch table is worth noting: three function pointers in an array replace a switch statement entirely, and adding a fourth operation means adding one array entry rather than editing control flow. This is the C ancestor of the virtual function table you will meet in the C++ sections.
T**. For function pointers, be able to write the declaration correctly (the parentheses around *fp are mandatory) and give a use case: qsort comparators or a dispatch table. Also state the three reasons to pass by pointer: modification, avoiding a large copy, and NULL as a sentinel.Create a free account to tick topics off, take notes as you read, watch the video lessons and get a day-by-day study plan built around your exam date.
Loading…