Function pointers in C
The previous article explored the nested switch approach to implement the state machine.
State Handler Approach:
The state handler approach involves creating a function for each state in a state machine. These functions are then called based on the current state of the machine. This approach is particularly useful when the number of states is small and the behavior of each state is simple. It allows for easy and straightforward maintenance.
The state handler approach, on the other hand, uses a set of functions to define the behavior of the state machine. Each function represents a state and defines the actions to be taken in that state. When an event occurs, the state machine calls the appropriate function to handle that event.
State Table Approach:
The state table approach involves creating a table that maps the current state to the next state based on some input. This approach is particularly useful when the number of states is large and the behavior of each state is complex. It also allows for easier maintenance as the behavior of each state is defined in the table and not in the code.
The state table and state handler approaches are both programming techniques commonly used in state machine design. In a state machine, the program’s behavior depends on its current state and the events that occur.
The state table approach uses a table to define the behavior of the state machine in each state. Each entry in the table contains a list of events and the corresponding actions that should be taken when those events occur in that state. The state machine can then use this table to determine its behavior at any given time.
The state table and state handler approach all use the Function pointer concepts. That’s why first, let’s explore Function pointers in C. So, afterward, I will continue with that approach.
Function Pointers in C
Function pointers in C are essentially pointers that point to the memory location of a function. These pointers can be used to call functions dynamically.
This is useful when we want to change the behavior of our program depending on the context or when we want to pass functions as parameters.
In computer programming, a function pointer is a type of pointer variable that stores the address of a function in memory. This allows the program to call the function indirectly, through the pointer, rather than directly by name.
Overall, the use of function pointers allows for a more flexible and modular design of the state machine.
Now, I have a function called ‘bar.’ The function does some operations like printf(“This is bar:%d\n”, i); as shown in figure 1.
When you load this program into a microcontroller, this function will get stored in memory. Strictly speaking, this function, I mean the code generated to represent this function will be stored in the code memory, which is nothing but a Flash memory. That means this function will have its own address. Functions address you can represent in a program by using its name.
First of all, let me explain the function pointer with an example. Let’s say you have some variable definitions, as shown in Figure 2.
→ int p; This is a variable definition, or you can also call it a declaration. Here, ‘p’ is a variable to hold ‘int’ type data.
→ int *q; This is also a variable definition. ‘q’ is a pointer variable to hold an address; the address points to the data type ‘int.’
→ q=&p; This represents the address of the ‘p’ variable, and ‘q’ is created to hold the address. That’s why I’m storing it like this. Similarly, you can use ‘&’ before a function name to represent its address, or you can use the function name as an address.
→ q = &bar; This actually gives warning .
First of all, to remove this compiler warnings about an unused variable. Let me write like this.
(void)p;
(void)q;
The warning is ‘assignment from incompatible pointer type.’ Here, it says ‘incompatible pointer type.’ Because, you see, ‘q’ is used to hold the address which points to the data, remember that. But &bar is an address entity, which doesn’t point to data; it points to the code. That’s why an address that points to the code you’re storing into a pointer variable created to hold the address that points to the data. That’s why this doesn’t make any sense.
Writing q= &bar doesn’t make any sense, which is wrong.
In C, you can create a special pointer variable, which you can use to hold the address of a function. Let’s create that.
Look at Figure 5;
Here also you need a pointer variable. A pointer variable is created by ‘*’ and ‘f,’ which you put inside the parenthesis. Like this (*f).
Since this is used to hold the address that points to the code and code doesn’t have any type. Data has different types, like int, char, unsigned int, long, float, double. Code doesn’t have different types. That’s why this doesn’t have any type, but this is used to hold the address of a function.
You have to mention the function parameter type and function return type here and end this variable definition with a semicolon. Like this void(*f)(void); So, this is also a variable definition.
‘f’ is a function pointer variable to hold the address of a function that doesn’t take any input arguments; that’s why ‘void’ doesn’t return any value. The return type is also a ‘void.’
‘f’ can be used to store the address of the bar function. f = &bar; or use f = bar;. But it’s better to use &bar because it gives you the notion that you are storing an address. That’s why it’s always better to use f = &bar; ‘f’ is called a function pointer because it points to the function as shown in Figure 6.
Since it points to the function, you can dereference that function and jump to that function by using the parenthesis like this (*f)(); This is to jump to the function pointed by ‘f.’ Instead of using this, you can also do something like this f();
But this gives you an impression that you are calling a function whose name is ‘f.’ So, this doesn’t give you the impression that you are using a function pointer to call a function. That’s why it is better to use the (*f)(10) method instead of f(10); So, that is just a recommendation.
int n = (*f)(10); int m = f(10); You can create a variable to catch the return value. It doesn’t let you call the function without mentioning the argument. You can mention that argument in the parenthesis, separated by commas.
That’s why, whenever you want to make a function pointer variable definition, make sure that you have to create a pointer variable, but you need to keep that inside the parenthesis.
Create one more variable called int(*f1)(int); So, you have to write this once again, and you can give a different name for the variable.
But instead of that, you can use the typedef version of this. So, I’ll show you that.
You create a typedef type. Like this typedef int(*f_t)(int); use the ‘typedef’ keyword and write your function pointer variable definition as shown in figure 1.
If you write like this, you can use the f_t name as an alias name for the int(*f_t)(int) definition. You can create more variables by using this alias name. I’ll use f_t to mark it as it’s a typedef name. You can use this typedef alias name to create as many function pointer variables as you want.
f_t f1, f2, f3, f4; all these are function pointer variables of f_t type. So, f_t is an alias name for typedef int(*f_t)(int);
Get the Embedded System Design using UML State Machines Full Course on Here
FastBit Embedded Brain Academy Courses
Click here: https://fastbitlab.com/course1