DEV Community

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

Posted on

1

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!

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

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]()();
}

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay