Programming in C

Function Pointers

Gerald Senarclens de Grancy

Purpose of Function Pointers

  • Allow treating functions as first class citizen
    • Being passed as an argument
    • Returned from a function
    • Assigned to a variable
  • Callback functions can be implemented
  • More functional programming techniques can be use
  • Functions can be members of structs
  • Prerequisite of object-like programming in C

Which Issues do Function Pointers Have?

  • Syntax can be hard to read and understand
  • C is still not an object oriented programming (OOP) language
    eg. encapsulation and inheritance are missing
The above problems are solved by C++ and other OOP languages
  • Use a proper OOP language if needed

Syntax

A function pointer is a pointer that points to a function

Type (*functionpointer)(Type arg1, Type arg2, ...);

The function pointer's type contains the arguments and return type of the function pointed to

Example

int sum(int a, int b);
int (*sum_ptr)(int,int); // declares a function pointer named `sum_ptr`
sum_ptr = ∑ // `&` is not required

The function pointer can then be used by de-referencing it

printf("The sum of 2 + 3 is %d\n", (*sum_ptr)(2, 3));  // `*` is not required

Complete Program

#include <stdio.h>

int sum(int a, int b) { return a + b; }
int main(void) {
int (*sum_ptr)(int,int); // declares a function pointer named `sum_ptr`
sum_ptr = sum;
printf("The sum of 2 + 3 is %d\n", sum_ptr(2, 3));
return 0;
}

Visualize execution

Application: Case Insensitive Sorting

qsort(.) in stdlib.h takes a function pointer as argument

void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));

strcasecmp(.) in strings.h performs case insensitive comparison

int strcasecmp(const char *s1, const char *s2);
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

int case_insensitive_compare(const void* a, const void* b) {
return strcasecmp(*(const char**)a, *(const char**)b);
}

int main(void) {
const char* names[] = {"Harry", "Anne", "Mary", "chris", "Pat", "eric", "Lily"};
qsort(names, 7, sizeof(const char*), case_insensitive_compare);
puts("names (sorted ascending)");
for (int i = 0; i < 7; ++i) {
puts(names[i]);
}
return 0;
}

Visualize execution

Application: Callback Functions

Download an internet resource without a callback (link against curl)

#include <stdio.h>
#include <curl/curl.h>

int main(void) {
CURL* curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://study.find-santa.eu/");
res = curl_easy_perform(curl);
if(res != CURLE_OK) // check of errors
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
return 0;
}
Download get_request.c

#include <stdio.h>
#include <curl/curl.h>
size_t write_response(void *ptr, size_t size, size_t nmemb, void *data) {
FILE* f = (FILE *) data;
puts("\033[32mwriting received data to `result.html`\033[0m");
return fwrite(ptr, size, nmemb, f);
}
int main(void) {
CURL* curl;
CURLcode res;
FILE* body;
body = fopen("result.html", "wb");
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://study.find-santa.eu/");
curl_easy_setopt(curl, CURLOPT_WRITEDATA, body);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response);
res = curl_easy_perform(curl);
if(res != CURLE_OK) // check of errors
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
fclose(body);
return 0;
}
Download get_with_callback.c

Application: Object Like Programming in C

typedef struct intarray IntArray;
struct intarray {
size_t reserved; // available storage
size_t elements; // current number of elements
int* array;

void (*push_back)(IntArray* self, int elem);
int (*sum)(const IntArray* self);
int (*count)(const IntArray* self, int elem);
IntArray* (*destruct)(IntArray* self);
};
IntArray* ia_construct();
#include <stdio.h>
#include <stdlib.h>

typedef struct intarray IntArray;
struct intarray {
size_t reserved; // available storage
size_t elements; // current number of elements
int* array;

void (*push_back)(IntArray* self, int elem);
long int (*sum)(const IntArray* self);
void (*print)(const IntArray* self);
IntArray* (*destruct)(IntArray* self);
};

IntArray* ia_destruct(IntArray* self) {
free(self->array);
self->array = (int*) NULL;
free(self);
return (IntArray*) NULL;
}

void ia_push_back(IntArray* self, int elem) {
if (self->elements == self->reserved) {
self->reserved *= 2;
self->array = realloc(self->array, self->reserved * sizeof(int));
}
self->array[self->elements] = elem;
self->elements++;
}

long int ia_sum(const IntArray* self) {
long int sum = 0;
for (size_t i = 0; i < self->elements; ++i) {
sum += self->array[i];
}
return sum;
}

void ia_print(const IntArray* self) {
if (!self->elements) {
printf("[]\n");
} else {
printf("[");
for (size_t i = 0; i < self->elements - 1; ++i) {
printf("%d,", self->array[i]);
}
printf("%d]\n", self->array[self->elements - 1]);
}
}

IntArray* ia_construct() {
IntArray* self = (IntArray*) malloc(sizeof(IntArray));
self->reserved = 100;
self->elements = 0;
self->array = (int*) malloc(self->reserved * sizeof(int));
self->print = ia_print;
self->sum = ia_sum;
self->push_back = ia_push_back;
self->destruct = ia_destruct;
return self;
}

int main(void) {
IntArray* ia = ia_construct();
ia->push_back(ia, 10);
ia->push_back(ia, 3);
ia->push_back(ia, 7);
ia->push_back(ia, 12);
ia->push_back(ia, 8);
ia->print(ia);
printf("The sum of all elements is %ld.\n", ia->sum(ia));
ia = ia->destruct(ia);
return 0;
}

Visualize execution

Exercise

Implement or rewrite the entire integer array functionality (second task of Dynamic Memory Management (Free Store) exercise using function pointers.

Questions
and feedback...