Inspired by comments to my answer here .
Is this sequence of steps legal in C standard (C11)?
void*
void*
Or equivalently as code:
void foo(void) { ... }
void bar(void) { ... }
typedef void (*voidfunc)(void);
voidfunc array[] = {foo, bar}; // Step 1
void *ptr1 = array; // Step 2
void *ptr2 = (char*)ptr1 + sizeof(voidfunc); // Step 3
voidfunc bar_ptr = *(voidfunc*)ptr2; // Step 4
I thought that this would be allowed, as the actual function pointers are only accessed through properly typed pointer. But Andrew Henle pointed out that this doesn't seem to be covered by Standard section 6.3.2.3: Pointers .
Your code is correct.
A pointer to a function is an object and you're casting a pointer to an object (a pointer to a function pointer) to void
pointer and back again; and then finally dereferencing a pointer to an object.
As for the char
pointer arithmetic, this is referred to by footnote 106 of C11:
106) Another way to approach pointer arithmetic is first to convert the pointer(s) to character pointer(s): In this scheme the integer expression added to or subtracted from the converted pointer is first multiplied by the size of the object originally pointed to, and the resulting pointer is converted back to the original type. For pointer subtraction, the result of the difference between the character pointers is similarly divided by the size of the object originally pointed to. When viewed in this way, an implementation need only provide one extra byte (which may overlap another object in the program) just after the end of the object in order to satisfy the ''one past the last element'' requirements.
Yes, the code is fine. There's various pitfalls and conversion rules at play here:
void*
is the generic pointer type for pointers to object type. Any pointer to object type may be converted to/from void*
, implicitly. (C17 6.3.2.3 §1). void*
or vice versa. (C17 6.3.2.3 §1) void(*)(void)
as a generic function pointer type. As long as you don't call the function through the wrong function pointer type, it is fine. (C17 6.3.2.3 §8) Function pointers point to functions, but they are objects in themselves, just like any pointer is. And so you can use a void*
to point at the address of a function pointer .
Therefore, using a void*
to point at a function pointer is fine. But not using it to point directly at a function. In case of void *ptr1 = array;
the array decays into a pointer to the first element, a void (**)(void)
(equivalent to voidfunc*
in your example). You may point at such a pointer to function-pointer with a void*
.
Furthermore, regarding pointer arithmetic:
void*
. (C17 6.3.2.2) Such arithmetic is a common non-standard extension that should be avoided. Instead, use a pointer to character type. Therefore, (char*)ptr1 + sizeof(voidfunc);
is also fine. You then convert from void*
to voidfunc*
, to voidfunc
which is the original function pointer type stored in the array.
As been noted in comments, you can improve readability of this code significantly by using a typedef
to a function type:
typedef void (voidfunc)(void);
voidfunc* array[] = {&foo, &bar}; // Step 1
void* ptr1 = array; // Step 2
void* ptr2 = (char*)ptr1 + sizeof(voidfunc*); // Step 3
voidfunc* bar_ptr = *(voidfunc**)ptr2; // Step 4
Pointer arithmetic on void*
is not in the C language. You re not doing it though, you are doing pointer arithmetic on char*
which is perfectly OK. You could have used char*
instead of void*
to begin with.
Andrew Helne seems to be missing the fact that a pointer to a function is an object, and its type is an object type. It is a plain simple fact, not something veiled in a shroud of mystery as some other commentators seem to imply. So his objection to casting a pointer to a function pointer is unfounded, as pointers to any object type can be cast to void*
.
However, the C standard doesn't seem to allow using (T*)((char*)p + sizeof(T))
in lieu of (p+1)
(where p
is a pointer to an element of an array of type T
), or at least I cannot find such permission in the text. Your code might not be legal because of that.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.