C course menu
Pointers and Functions in C: Output Parameters, Returning Pointers and Function Pointers
Use pointers with functions in C: output parameters, const-correct pointer parameters, returning pointers safely and the dangling-pointer bug, function pointers, callbacks, and sorting with qsort and a comparison function.
On this page
Pointers and functions work together in three ways: functions can take pointers, return pointers, and be pointed to.
Output parameters#
A function can only return one value, but it can write results through pointer parameters. Library functions do this all the time, for example scanf and strtol:
#include <stdio.h>
int parse_time(const char *text, int *hours, int *minutes) {
if (sscanf(text, "%d:%d", hours, minutes) != 2) return 0;
return *hours >= 0 && *hours < 24 && *minutes >= 0 && *minutes < 60;
}
int main(void) {
int h, m;
if (parse_time("09:45", &h, &m)) printf("%d minutes after midnight\n", h * 60 + m);
if (!parse_time("25:00", &h, &m)) printf("invalid time\n");
return 0;
}585 minutes after midnight
invalid timeThe return value reports success; the pointers carry the data. This is a very common C pattern.
const and pointers#
Where const goes changes what is protected:
Read from right to left: const int *p is "p is a pointer to an int that is const". Use const on parameters the function only reads.
Returning pointers#
A function may return a pointer to data that outlives the call: an element of an array the caller passed in, a static variable, or memory from malloc. It must never return the address of its own local variable, which disappears when the function returns:
int *broken(void) {
int local = 42;
return &local;
}This compiles, but GCC warns function returns address of local variable. The caller receives a dangling pointer: an address whose variable no longer exists. Treat this warning as an error.
Returning a pointer into the caller's data is fine:
#include <stdio.h>
int *find_max(int *a, int n) {
int *best = a;
for (int *p = a + 1; p < a + n; p++) {
if (*p > *best) best = p;
}
return best;
}
int main(void) {
int scores[] = {55, 91, 73, 88};
int *top = find_max(scores, 4);
*top = 100; /* change it in place */
printf("%d %d %d %d\n", scores[0], scores[1], scores[2], scores[3]);
return 0;
}55 100 73 88Function pointers#
A function also has an address, and a function pointer stores it. The syntax puts the name in parentheses with a *:
#include <stdio.h>
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
int apply(int (*op)(int, int), int x, int y) {
return op(x, y);
}
int main(void) {
int (*f)(int, int) = add;
printf("%d\n", f(3, 4));
f = multiply;
printf("%d\n", f(3, 4));
printf("%d\n", apply(add, 10, 5));
return 0;
}7
12
15Passing a function to another function is called a callback. A typedef makes the type easier to read: typedef int (*BinaryOp)(int, int);.
An array of function pointers works like a simple dispatch table:
#include <stdio.h>
typedef double (*Op)(double, double);
double add(double a, double b) { return a + b; }
double sub(double a, double b) { return a - b; }
double mul(double a, double b) { return a * b; }
int main(void) {
Op ops[] = {add, sub, mul};
const char *names[] = {"+", "-", "*"};
for (int i = 0; i < 3; i++) {
printf("8 %s 2 = %.0f\n", names[i], ops[i](8, 2));
}
return 0;
}8 + 2 = 10
8 - 2 = 6
8 * 2 = 16Sorting with qsort#
The standard qsort (from <stdlib.h>) sorts any array. You give it a callback that compares two elements, passed as const void *, returning a negative number, zero or a positive number:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int by_value(const void *a, const void *b) {
int x = *(const int *)a;
int y = *(const int *)b;
return (x > y) - (x < y); /* avoids overflow from x - y */
}
int by_name(const void *a, const void *b) {
const char *x = *(const char *const *)a;
const char *y = *(const char *const *)b;
return strcmp(x, y);
}
int main(void) {
int nums[] = {42, -7, 19, 3, 88};
qsort(nums, 5, sizeof nums[0], by_value);
for (int i = 0; i < 5; i++) printf("%d ", nums[i]);
printf("\n");
const char *langs[] = {"python", "c", "java", "cpp"};
qsort(langs, 4, sizeof langs[0], by_name);
for (int i = 0; i < 4; i++) printf("%s ", langs[i]);
printf("\n");
return 0;
}-7 3 19 42 88
c cpp java python void * is a generic pointer that can hold any object address; you convert it back to the real type before using it.
Practice#
Practice exercise
Write void for_each(int *a, int n, void (*fn)(int *)) that calls fn on every element, and two callbacks: square (squares the value) and print (prints it followed by a space). Use them on {1, 2, 3, 4}.
Show solution
#include <stdio.h>
void for_each(int *a, int n, void (*fn)(int *)) {
for (int i = 0; i < n; i++) fn(&a[i]);
}
void square(int *x) { *x = *x * *x; }
void print(int *x) { printf("%d ", *x); }
int main(void) {
int nums[] = {1, 2, 3, 4};
for_each(nums, 4, square);
for_each(nums, 4, print);
printf("\n");
return 0;
}1 4 9 16 Key takeaway#
Finished this lesson?