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 C-structs
  • Prerequisite of object-like programming in C

Which Issues do Function Pointers Have?

  • Syntax can be hard to read and understand
  • Function pointers do not make C an object oriented language
    (advanced concepts like encapsulation and inheritance are missing)
  • When object oriented programming (OOP) is desired,
    use a proper OOP language like C++

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));
printf("The sum of 2 + 3 is %d\n", (sum_ptr)(2, 3));  // `*` is not required
printf("The sum of 2 + 3 is %d\n", sum_ptr(2, 3));  // `()` are 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;  // same as `sum_ptr = &sum;`
  printf("The sum of 2 + 3 is %d.\n", (*sum_ptr)(2, 3));
  printf("The sum of 2 + 3 is %d.\n", (sum_ptr)(2, 3));  // `*` is not required
  printf("The sum of 2 + 3 is %d.\n", sum_ptr(2, 3));  // `()` are not required
  return 0;
}

Visualize execution

Download basics.c

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> (resp. <cstring>) 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 using C-structs

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...