简体   繁体   English

你可以将“指向函数指针的指针”转换为void *

[英]Can you cast a “pointer to a function pointer” to void*

Inspired by comments to my answer here . 通过评论我的回答启发这里

Is this sequence of steps legal in C standard (C11)? 这一系列步骤在C标准(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* 获取指向第一个条目的指针并将该指针转换为函数指针 void*
  3. Perform pointer arithmetic on that void* 对该void*执行指针算术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 . 但Andrew Henle指出, 标准部分6.3.2.3似乎没有涵盖这一点:指针

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; 指向函数的指针是一个对象,并且您正在向对象 (指向函数指针的指针)转换指针以void指针void并再次返回; and then finally dereferencing a pointer to an object. 然后最终取消引用指向对象的指针。

As for the char pointer arithmetic, this is referred to by footnote 106 of C11: 至于char指针算术,这由C11的脚注106引用:

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. 106)接近指针运算的另一种方法是首先将指针转换为字符指针:在此方案中,首先将转换后的指针中添加或减去的整数表达式乘以最初指向的对象的大小。 ,并将结果指针转换回原始类型。 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: 这里有各种陷阱和转换规则:

  • C splits all types in two main categories: objects and functions. C将所有类型拆分为两个主要类别:对象和函数。 A pointer to a function is a scalar type which in turn is an object. 指向函数的指针是标量类型 ,而标量类型又是一个对象。 (C17 6.2.5) (C17 6.2.5)
  • void* is the generic pointer type for pointers to object type. void*是指向对象类型的指针的通用指针类型。 Any pointer to object type may be converted to/from void* , implicitly. 任何指向对象类型的指针都可以隐式转换为void* (C17 6.3.2.3 §1). (C176.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. 因此,函数指针不能转换为void* ,反之亦然。 (C17 6.3.2.3 §1) (C176.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. 但是,任何函数指针类型都可以转换为另一个函数指针类型并返回,允许我们使用类似void(*)(void)作为泛型函数指针类型。 As long as you don't call the function through the wrong function pointer type, it is fine. 只要你不通过错误的函数指针类型调用函数,就可以了。 (C17 6.3.2.3 §8) (C176.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 . 因此,您可以使用void*指向函数指针的地址

Therefore, using a void* to point at a function pointer is fine. 因此,使用void*指向函数指针是很好的。 But not using it to point directly at a function. 但不是用它直接指向一个函数。 In case of void *ptr1 = array; 如果是void *ptr1 = array; the array decays into a pointer to the first element, a void (**)(void) (equivalent to voidfunc* in your example). 数组衰减成指向第一个元素的指针,一个void (**)(void) (相当于你的例子中的voidfunc* )。 You may point at such a pointer to function-pointer with a void* . 你可以指向一个带有void*函数指针这样的指针。

Furthermore, regarding pointer arithmetic: 此外,关于指针运算:

  • No pointer arithmetic can be performed on a void* . 无法对void*执行指针运算。 (C17 6.3.2.2) Such arithmetic is a common non-standard extension that should be avoided. (C17 6.3.2.2)这种算术是应该避免的常见非标准扩展。 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). 作为一种特殊情况,指向字符类型的指针可用于迭代任何对象(C176.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). 除了关于对齐的问题之外,这样做是明确定义的,并且不违反“严格的指针别名”,如果你取消引用字符指针(C176.5§7)。

Therefore, (char*)ptr1 + sizeof(voidfunc); 因此, (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. 然后,从转换void*voidfunc* ,以voidfunc这是存储阵列中的原始函数指针类型。

As been noted in comments, you can improve readability of this code significantly by using a typedef to a function type: 正如在注释中所指出的,通过对函数类型使用typedef ,可以显着提高此代码的可读性:

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. void*上的指针算法不在C语言中。 You re not doing it though, you are doing pointer arithmetic on char* which is perfectly OK. 你没有这样做,你在char*上做指针运算,这是完全可以的。 You could have used char* instead of void* to begin with. 您可以使用char*而不是void*来开始。

Andrew Helne seems to be missing the fact that a pointer to a function is an object, and its type is an object type. Andrew Helne似乎错过了一个指向函数的指针是一个对象的事实,它的类型是一个对象类型。 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* . 因此,他反对投射指向函数指针的指针是没有根据的,因为指向任何对象类型的指针都可以转换为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. 但是,C标准似乎不允许使用(T*)((char*)p + sizeof(T))代替(p+1) (其中p是指向类型数组的元素的指针) T ),或者至少我在文中找不到这样的许可。 Your code might not be legal because of that. 因此,您的代码可能不合法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM