Functions pointers

Location
  1. Courses

    /

  2. Complete C++ Course

    /

  3. The basics

    /

  4. Function pointers

The memory address of functions

Like variables, functions have a memory address. We can retrieve it by casting the function into a pointer. To get its address, we can add an ampersand & before the name of the function, but this is optional.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream> void func() { std::cout << "Function 'func'.\n"; } int main() { void *ptr; // Both the following lines are equivalent. ptr = (void*)func; ptr = (void*)&func; return 0; }

We retrieved the address of the function into a pointer, but there is a problem. We can not call a function through a normal pointer. We must define a function pointer, which tells the return type and the argument types of the function, so the compiler knows how to call it.

Defining function pointers

To define a function pointer, we must write the return type of the function, then, between parentheses (), an asterisk * followed by the name of the pointer, and finally, between parentheses (), the argument types, separated by commas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream> void func() { std::cout << "Function 'func'.\n"; } int add(int a, int b) { return a + b; } int main() { /* Defines a pointer to a function that returns nothing and takes no argument. */ void (*ptr)(); /* Defines a pointer to a function that return a value and takes two arguments, of type int. */ int (*ptr2)(int, int); // Makes 'ptr' points to 'func'. ptr = func; // Or. ptr = &func; // Makes 'ptr2' points to 'add'. ptr2 = add; return 0; }

Calling functions through function pointers

Calling a function through a function pointer is extremely easy. This is done the same way as if we called the function directly: We write the name of the pointer and then, between parentheses (), we write the arguments, separated by commas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream> void func() { std::cout << "Function 'func'.\n"; } int add(int a, int b) { return a + b; } int main() { void (*ptr)(); int (*ptr2)(int, int); ptr = func; ptr2 = add; // Calls the function 'func' using the function pointer 'ptr'. ptr(); // Calls the function 'add' using the function pointer 'ptr2'. std::cout << ptr2(13, 21) << std::endl; return 0; }

Note that it is also allowed to call the function pointed by a function pointer by writing, inside parentheses (), an asterisk * followed by the name of the pointer, and then write the arguments of the function, inside parentheses () and separated by commas.

(*ptr)(); std::cout << (*ptr2)(13, 21) << std::endl;

Casting pointers to function pointers

It is possible to call functions from pointers of any type, by casting them into function pointers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream> float pow2(float a) { return a * a; } int main() { void *ptr = (void*)pow2; std::cout << // We cast 'ptr' into a function pointer so we can call the function at the address it points to. ((float (*)(float)) ptr) (8.0f) << std::endl; return 0; }

When are function pointers useful?

An example where function pointers are useful would be the creation of graphical user interfaces. A structure representing a push button could contain a function pointer, pointing to the function the programmer wants to be executed when the button is clicked on, as a member.

On other example would be if we want to execute binary code read from a file. We could read the binary code into an array of bytes and then execute it by making a function pointer points to it and then call it. Note, however, that the binary code must represent a function that is compatible with how the C/C++ languages pass arguments to functions.

Non-executable memory

Note that memory spaces may be marked as code or data memory. It is not possible to make the processor execute code that is stored inside a memory space marked as data memory by the operating system. Static arrays are non-executable memory spaces and both the C/C++ standard libraries do not provide tools to create arrays of bytes that are executable, nor to mark a memory space as executable. To do so, we would have to use functions from other libraries (For example, the library of the operating system).