Because a function has no way of knowing how it was called in C.
It only knows that it was called, and the parameters it was called with. (The function can also access any variables visible in the scope it was implemented in, but that won't help here: via function pointers, you can call the function from scopes that are utterly unreachable from the scope the function was implemented in.)
In particular, the function called has no way of knowing whether it was called directly (
write32(...)) or via a function pointer (
dev->dev->write32(...)).
Let's look at a complete example you can compile and run:
Code:
#include <stdlib.h>
#include <stdio.h>
struct inner {
int (*kick)(int);
int (*punt)(struct inner *, int);
int id;
};
struct outer {
struct inner *ptr;
};
static int kick1(int x)
{
return x;
}
static int kick2(int x)
{
return x*x;
}
static int punt(struct inner *ptr, int x)
{
return ptr->id + x;
}
int main(void)
{
struct outer *var_a, *var_b;
/* Initialize var_a. */
var_a = malloc(sizeof *var_a);
if (!var_a) {
fprintf(stderr, "Out of memory.\n");
return EXIT_FAILURE;
}
var_a->ptr = malloc(sizeof *(var_a->ptr));
if (!var_a->ptr) {
fprintf(stderr, "Out of memory.\n");
return EXIT_FAILURE;
}
var_a->ptr->id = 1;
var_a->ptr->kick = kick1;
var_a->ptr->punt = punt;
/* Initialize var_b. */
var_b = malloc(sizeof *var_b);
if (!var_b) {
fprintf(stderr, "Out of memory.\n");
return EXIT_FAILURE;
}
var_b->ptr = malloc(sizeof *(var_b->ptr));
if (!var_b->ptr) {
fprintf(stderr, "Out of memory.\n");
return EXIT_FAILURE;
}
var_b->ptr->id = 2;
var_b->ptr->kick = kick2;
var_b->ptr->punt = punt;
printf("var_a = (struct outer *)0x%llx\n", (unsigned long long)var_a);
printf("var_a->ptr = (struct inner *)0x%llx\n", (unsigned long long)(var_a->ptr));
printf("var_a->ptr->kick = function pointer to 0x%llx\n", (unsigned long long)(void (*)(void))(var_a->ptr->kick));
printf("var_a->ptr->punt = function pointer to 0x%llx\n", (unsigned long long)(void (*)(void))(var_a->ptr->punt));
printf("var_a->ptr->id = %d\n", var_a->ptr->id);
printf("\n");
printf("var_b = (struct outer *)0x%llx\n", (unsigned long long)var_b);
printf("var_b->ptr = (struct inner *)0x%llx\n", (unsigned long long)(var_b->ptr));
printf("var_b->ptr->kick = function pointer to 0x%llx\n", (unsigned long long)(void (*)(void))(var_b->ptr->kick));
printf("var_b->ptr->punt = function pointer to 0x%llx\n", (unsigned long long)(void (*)(void))(var_b->ptr->punt));
printf("var_b->ptr->id = %d\n", var_b->ptr->id);
printf("\n");
printf("var_a->ptr->kick(3) returns %d.\n", var_a->ptr->kick(3));
printf("var_a->ptr->punt(var_a->ptr, 3) returns %d.\n", var_a->ptr->punt(var_a->ptr, 3));
printf("\n");
printf("var_b->ptr->kick(3) returns %d.\n", var_b->ptr->kick(3));
printf("var_b->ptr->punt(var_b->ptr, 3) returns %d.\n", var_b->ptr->punt(var_b->ptr, 3));
printf("\n");
printf("kick1(3) returns %d\n", kick1(3));
printf("kick2(3) returns %d\n", kick2(3));
printf("\n");
printf("var_a->ptr->punt(var_a->ptr, 6) returns %d.\n", var_a->ptr->punt(var_a->ptr, 6));
printf("var_b->ptr->punt(var_b->ptr, 6) returns %d.\n", var_b->ptr->punt(var_b->ptr, 6));
printf("var_a->ptr->punt(var_b->ptr, 6) returns %d.\n", var_b->ptr->punt(var_a->ptr, 6));
printf("var_b->ptr->punt(var_a->ptr, 6) returns %d.\n", var_a->ptr->punt(var_b->ptr, 6));
return EXIT_SUCCESS;
}
Compiled an run, it outputs something like
Code:
var_a = (struct outer *)0x21bd010
var_a->ptr = (struct inner *)0x21bd030
var_a->ptr->kick = function pointer to 0x40064d
var_a->ptr->punt = function pointer to 0x400669
var_a->ptr->id = 1
var_b = (struct outer *)0x21bd050
var_b->ptr = (struct inner *)0x21bd070
var_b->ptr->kick = function pointer to 0x400659
var_b->ptr->punt = function pointer to 0x400669
var_b->ptr->id = 2
var_a->ptr->kick(3) returns 3.
var_a->ptr->punt(var_a->ptr, 3) returns 4.
var_b->ptr->kick(3) returns 9.
var_b->ptr->punt(var_b->ptr, 3) returns 5.
kick1(3) returns 3
kick2(3) returns 9
var_a->ptr->punt(var_a->ptr, 6) returns 7.
var_b->ptr->punt(var_b->ptr, 6) returns 8.
var_a->ptr->punt(var_b->ptr, 6) returns 7.
var_b->ptr->punt(var_a->ptr, 6) returns 8.
If you examine the code, you can see that in any
kick function, you do not have access to the
id member.
To get access to the
id member, you need to supply a pointer to the
struct inner structure to the function, like I do with the
punt functions.
Above, there is only one
punt() function implemented; both
var_a->ptr->punt and
var_b->ptr->punt point to the same function (which happened to be at address 0x400669).
Think of this as the same
punt() being used in two different games, and its behaviour varies depending on the game it is used in. (The game being analogous to the
struct inner structure here.)
However,
kick1() and
kick2() differ (maybe your foot is positioned differently between the two), but have no reference to any game, no access to any
ids, so they always behave the same way.
Look closely at the last two lines of the output, and you'll notice that I deliberately provided the
wrong pointer to the
punt() function.
[I]It does not matter which pointer I used to reach the
punt function; it will use the
id member from its first parameter.
If you omit the first parameter to the
punt functions, you get the exact same interface as the
kick functions have. However, the
kick functions do not have any access to the
id member. Indeed, the example even calls
kick1() and
kick2() directly, which should be proof enough that there is no relevant
id at all.