受到对我的答案here的评论的启发。
此步骤顺序在C标准(C11)中合法吗?
void*
void*
执行指针算术或等效地作为代码:
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
我认为这是允许的,因为实际的函数指针只能通过正确键入的指针来访问。但是安德鲁·亨勒(Andrew Henle)指出,Standard section 6.3.2.3: Pointers似乎没有涵盖这一点。
答案 0 :(得分:8)
您的代码正确。
指向函数的指针是一个对象,您正在将指向 object 的指针(指向函数指针的指针)强制转换为void
指针,然后再次返回;然后最终取消引用指向对象的指针。
对于char
指针算法,这由C11的footnote 106引用:
106)进行指针算术的另一种方法是首先将指针转换为字符指针:在这种方案中,首先将转换后的指针添加或减去的整数表达式乘以对象的大小最初指向,并且结果指针将转换回原始类型。对于指针减法,字符指针之间的差异结果类似地除以最初指向的对象的大小。当以这种方式查看时,实现只需在对象结束之后提供一个额外的字节(可能与程序中的另一个对象重叠)即可满足“最后一个元素过去”的要求。
答案 1 :(得分:4)
是的,代码很好。这里有各种陷阱和转换规则:
void*
是指向对象类型的指针的通用指针类型。任何指向对象类型的指针都可以隐式地与void*
之间进行转换。 (C17 6.3.2.3§1)。void*
,反之亦然。 (C17 6.3.2.3§1)void(*)(void)
之类的东西用作通用函数指针类型。只要您不通过错误的函数指针类型调用函数,就可以了。 (C17 6.3.2.3§8)函数指针指向函数,但是它们本身就是对象,就像任何指针一样。因此,您可以使用void*
指向函数指针的地址。
因此,使用void*
指向函数指针是可以的。但是不能用它直接指向一个函数。在void *ptr1 = array;
的情况下,数组衰减为指向第一个元素void (**)(void)
的指针(在您的示例中等效于voidfunc*
)。您可以使用void*
指向此类函数指针。
此外,关于指针算法:
void*
上执行指针算术运算。 (C17 6.3.2.2)这种算术运算是常见的非标准扩展,应避免。而是使用指向字符类型的指针。因此,(char*)ptr1 + sizeof(voidfunc);
也可以。然后,您从void*
转换为voidfunc*
,转换为voidfunc
,这是存储在数组中的原始函数指针类型。
如注释中所述,可以通过对函数类型使用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
答案 2 :(得分:0)
void*
上的指针算术不是C语言。不过,您没有这样做,而是在char*
上进行指针算术,这完全可以。首先,您可以使用char*
而不是void*
。
安德鲁·海恩(Andrew Helne)似乎缺少以下事实:指向函数的指针是对象,并且其类型是对象类型。这是一个简单的事实,并非像其他评论员所暗示的那样笼罩在神秘之中。因此,他反对将指针转换为函数指针的说法是没有根据的,因为指向任何对象类型的指针都可以转换为void*
。
但是,C标准似乎不允许使用(T*)((char*)p + sizeof(T))
代替(p+1)
(其中p
是指向类型{{1}的数组的元素的指针) }),或者至少我在文本中找不到此类许可。因此,您的代码可能不合法。