Can you cast a “pointer to a function pointer” to void*

谁说我不能喝 提交于 2019-12-07 13:02:32

问题


Inspired by comments to my answer here.

Is this sequence of steps legal in C standard (C11)?

  1. Make an array of function pointers
  2. Take a pointer to the first entry and cast that pointer to function pointer to void*
  3. Perform pointer arithmetic on that void*
  4. Cast it back to pointer to function pointer and dereference it.

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.


回答1:


Yes, the code is fine. There's various pitfalls and conversion rules at play here:

  • C splits all types in two main categories: objects and functions. A pointer to a function is a scalar type which in turn is an object. (C17 6.2.5)
  • 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).
  • No such generic pointer type exists for pointers to function type. Thus a function pointer cannot be converted to a void* or vice versa. (C17 6.3.2.3 §1)
  • However, any function pointer type can be converted to another function pointer type and back, allowing us to use something like for example 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:

  • No pointer arithmetic can be performed on a 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.
  • A pointer to character type may, as a special case, be used to iterate over any object (C17 6.2.3.3 §7). Apart from concerns regarding alignment, doing so is well-defined and does not violate "strict pointer aliasing", should you de-reference the character pointer (C17 6.5 §7).

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



回答2:


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.




回答3:


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.



来源:https://stackoverflow.com/questions/53033813/can-you-cast-a-pointer-to-a-function-pointer-to-void

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!