DEV Community

Cover image for Types in C: The void (*(*[])())()
Lewis Lloyd
Lewis Lloyd

Posted on

Types in C: The void (*(*[])())()

#c

Introduction

You've heard of an int and a string, but have you heard of a void (*(*[])())()?

Yes, this is a real type declaration in C, and we'll see just how to use it and how to deal with complicated types.

Program Memory

One of the C language's superpowers is its low-level memory management.

You've likely manipulated memory before. Most languages allow you to pass variables by reference (i.e. its memory address) as well as by value (i.e. a copy is made so that there are no side effects).

Pass by Reference vs. Pass by Value Analogy

C gives developers a standard low-level interface to program memory on both the heap and the stack.

An important part of this interface is pointer types.

Pointers

Put simply, a pointer is a variable that stores a memory address to another variable.

Since a pointer is a variable, we can have a pointer to a pointer. And a pointer to a pointer to a pointer. And so on, and forever. See this diagram for reference:

Pointer to a Pointer to a Pointer to a Variable


The void (*(*[])())()

"What?"

Using the awesome converter at cdecl.org, we can see what this type declaration really means.

Conversion with cdecl

Let's work backwards to understand what's going on.

Breakdown

         foo            -- foo
         foo[]          -- is an array
        *foo[]          -- of pointers
       (*foo[])()       -- to functions
      *(*foo[])()       -- returning pointers
     (*(*foo[])())()    -- to functions
void (*(*foo[])())();   -- returning void
Enter fullscreen mode Exit fullscreen mode

Now that we understand the individual components, let's create it!

Function returning Void

A function that doesn't return anything (i.e. a procedure) is a void function.

For this, we'll define a function that prints "Apple!".

void apple() {
    printf("Apple!\n");
}
Enter fullscreen mode Exit fullscreen mode

Pointer to a Function returning Void

This is simple. It's a pointer to the function that we just declared.

&apple;
Enter fullscreen mode Exit fullscreen mode

Function returning a Pointer to a Function returning Void

We need a function that returns &apple. The hard part here is the type signature.

void (*pointer_to_apple())() {
    return &apple;
}
Enter fullscreen mode Exit fullscreen mode

One mistake I made initially was declaring this as void *pointer_to_apple(). This would be incorrectly returning a Pointer to Void, as opposed to a Pointer to a Function returning Void.

You may wish to look at the syntax of Function Pointers in order to understand this better.

Pointer to a Function returning a Pointer to a Function returning Void

This is simple again. It's a pointer to this new function.

&pointer_to_apple;
Enter fullscreen mode Exit fullscreen mode

Array of Pointers to Functions returning a Pointer to a Function returning Void

The final step, and an easy one at that. Pretending we defined two similar sets of functions and pointers, we can an array of these new pointers.

{
    &pointer_to_apple,
    &pointer_to_banana,
    &pointer_to_cherry
};
Enter fullscreen mode Exit fullscreen mode

Let's define a variable with this array.

Our variable foo is an array of of type void (*(*[])())() with a size of 3.

void (*(*foo[3])())() = {
    &pointer_to_apple,
    &pointer_to_banana,
    &pointer_to_cherry
};
Enter fullscreen mode Exit fullscreen mode

Using this Array

We can now iterate through the array to print our various fruits.

The only boilerplate here is getting the size of a void (*(*[])())(). This is standard for iterating through arrays in C, as using the sizeof operator to determine the size of arrays is error prone.

size_t n = sizeof(foo)/sizeof(foo[0]);
Enter fullscreen mode Exit fullscreen mode

We can now iterate through the array. As each element is effectively a pointer to a function returning a pointer to a function, we use foo[i]()(), which calls each function pointed to.

for (int i = 0; i < n; ++i) {
    foo[i]()();
}
Enter fullscreen mode Exit fullscreen mode

Let's check the output.

>>> Apple!
>>> Banana!
>>> Cherry!
>>> 
>>> Process finished with exit code 0
Enter fullscreen mode Exit fullscreen mode

Success!

Example Code

You can check this code out in my GitHub repository.

#include <stdio.h>

void apple() {
    printf("Apple!\n");
}

void banana() {
    printf("Banana!\n");
}

void cherry() {
    printf("Cherry!\n");
}

void (*pointer_to_apple())() {
    printf("Getting pointer to apple.\n");
    return &apple;
}

void (*pointer_to_banana())() {
    printf("Getting pointer to banana.\n");
    return &banana;
}

void (*pointer_to_cherry())() {
    printf("Getting pointer to cherry.\n");
    return &cherry;
}

int main() {
    void (*(*foo[3])())() = {
            &pointer_to_apple,
            &pointer_to_banana,
            &pointer_to_cherry
    };
    size_t n = sizeof(foo) / sizeof(foo[0]);
    for (int i = 0; i < n; ++i) {
        foo[i]()();
    }
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Hey, guys! Thank you for reading. I hope that you enjoyed this.

Keep up to date with me:

Catch you around!

Top comments (1)

Collapse
 
pentacular profile image
pentacular • Edited
// The correct answer is that this is an opportunity to learn typedef. :)

#include <stdio.h>

void apple() { printf("apple\n"); }

typedef void op();

// And that a function expression evaluates to a pointer to that function.

op *iapple() { return apple; }

typedef op *iop();

// So we don't need those &s.

iop *ops[] = { iapple };

// Hurrah.

int main() {
  ops[0]()();
}