Check the first part here.
To take it up from where we left off – function pointers!
Understanding/analyzing declarations in C takes on a new meaning when function pointers are added to the mix.
At first, the syntax used to declare function pointer variables may look a little unusual, but a second look at it suddenly will make sense.
And to recap, the three rules for analyzing declarations in C are given below:
The three rules that define the precedence of symbols/operators are:
First, let us try to figure out the following declarations:
char *strstr (char *, char *);
Rule #1 is not applicable since there are no parentheses grouping together any symbol and operator.
The next highest precedence in this case is going to be for the parentheses associated with functions (rule #2, parentheses that contain the list of arguments to the function). So strstr is a function. And the return value is char *. Note that the * is associated with the return type and not the symbol.
To complete the declaration we add that strstr is a function that accepts two char pointers as arguments and returns a char *.
But what about this?
int (*scmp) (char *, char *);
This is where we get started with function pointers.
If we are going to have a pointer variable that can store the address of a function, we will need to get the address of that function (starting address). So, there must be a way to get that (otherwise what is the point in having a function pointer?).
As in the case of arrays, the name of the function is its starting address. We will see how this works out in another post.
But that addresses only the first problem. Now for the second one and that one is a bit more complicated. It has to do with what was referred to as “esoteric” in the previous post. If we have to declare a variable in C, we need to know its type. Now that brings up the million dollar question – what in heaven’s name is the type of a function? One answer to that question is to say that the type of a function is its signature.
That really makes things crystal clear. Or not! Let us see what the signature of a function is. Consider a function like strcmp (), whose prototype declaration is given as:
int strcmp (char *, char *);
To get the signature of any function, just specify the types of the arguments received by the function and the type of the value returned by the function. So the signature of strcmp () would be:
int (char *, char *).
And as mentioned already, that gives the type of the function. Needless to say, that doesn’t look like any of the usual types in C.
Now that we figured out how to get the type of the function, how do we declare a function pointer variable that can be initialised properly?
Since we want to declare a pointer, obviously we will have the * operator and a symbol. If the symbol is assumed to be scmp, then the pointer variable should be declared as (* scmp). This is because when applying our three rules, rule #1 will get applied thus giving this the highest priority with rest of the declaration getting lower priority.
If we were to follow the standard C syntax for variable declaration, (type followed by the symbol like int inx), the function pointer declaration for strcmp () would end up looking like:
int (char *, char *) (*scmp);
The question now is, does this really convey the meaning that scmp is a pointer to a function that accepts two char pointers and returns an int? IMHO, it doesn’t.
One way out would be to make the declaration look like a function prototype declaration that clearly explicates the argument type list and the return type. And that means putting the grouped pair of the symbol and operator in the middle of the type of the function. And, voila, we get what we had seen earlier:
int (*scmp) (char *, char *);
And now let us apply the rules to this and see whether we get we want.
Rule #1 of highest precedence applies in this case since there is a pair of parentheses grouping together a symbol, scmp and an operator, *. So scmp is a pointer rather than a function that returns int * as a return type.
Next highest precedence is for parentheses collecting together the types of arguments. Since there is a pair of parentheses collecting together the types of arguments to the function, scmp is a pointer to a function.
Now we just complete the declaration by mentioning the argument types and the return type. So scmp is a pointer to a function that accepts to char pointers and returns an int.
Let us take an example that uses a pointer to call the strstr () library function. The code is below.
#include <stdio.h>
#include <string.h>
int main (int ac, char *av[])
{
char *found = NULL;
char * (*fptr) (char *, char *);
fptr = strstr;
if (ac == 3) {
found = fptr (av[1], av[2]);
if (found)
printf (“%s found in %s as %s\n“, av[2], av[1], found);
else
printf (“%s not found in %s\n“, av[2], av[1]);
}
else {
printf (“%s <hay_stack> <needle>\n“, av[0]);
}
return 0;
}
NOTE: If you try to compile the program and execute it, you will get a warning because of a slight mismatch in types, but the program will work as expected. If you want to avoid the warning also (which is the correct way to do it), then all you need to do is use the const keyword with both the arguments to the function as below.
char * (*fptr) (const char *, const char *);
Ready to take your skills to the next level? Explore job opportunities at Vayavya, where innovation meets talent. Apply now and join our dynamic team!
Venu Kolathur is Chief Architect and Co-Founder at Vayavya Labs and has over 38 years of industry & academic experience. He is responsible for product technology road-map, and design strategies.