在C语言中,使用指向指针的指针进行类型转换是合法的吗?

6
在一个C程序中,我有一个struct结构体。
typedef struct {
    void *payload; // opaque, real type known to callbacks

         ... some stuff ...
} MiddleMan;

为了增加类型安全性,我可能会创建getter和setter。
CbData *get_cb_data(const MiddleMan *mm){ return mm->payload; }   
void set_cb_data(MiddleMan *mm, CbData *cbd){ mm->payload = cbd; }

或者我可以尝试使用单个基于指针的访问器来完成

CbData **cb_data(MiddleMan *mm){return (CbData**)&mm->payload;}

现在第二个解决方案看起来比第一个更靠不住,它也限制了用户对非const mm 的访问,即使他们只想读取。但我的问题是它是否符合C语言规范?我相当确定在任何void*CbData*大小和格式相同的体系结构中都可以使用它。但是有没有人可以清晰地说明为什么这在一般情况下是(或不是)有效的?

1
这在C语言中是合法的。void *CbData *始终具有相同的大小 - 两者都是指针类型。很抱歉,我不明白为什么你上面写的**实现会有疑问。 - Manoj Awasthi
3
在某些奇怪的体系结构上,指针的大小取决于它们所指向的内容。void *应该足够大以涵盖所有可能性,但是CbData *可能会更小,这取决于CbData实际上是什么。 - Adrian Ratnapala
你能否举几个奇怪的架构的例子?我很好奇。 - Manoj Awasthi
@ManojAwasthi,我很高兴我不知道它们中的任何一个(我在comp.lang.c上听说过它们)。也许一些旧的超级计算机将数字向量放在与簿记整数不同的宇宙中。16位DOS / Windows程序之间有“近”和“远”的区别,但这与所指向的类型无关。 - Adrian Ratnapala
1个回答

4
一般来说,你不应该那样做。一个 void** 可能具有与 CbData** 不同的对齐要求。显式转换可能会产生不同的地址。
C标准6.2.5.27:

指向 void 的指针应具有与字符类型的指针相同的表示和对齐要求39)。类似地,指向兼容类型的限定或非限定版本的指针应具有相同的表示和对齐要求。所有指向结构类型的指针应具有彼此相同的表示和对齐要求。所有指向联合类型的指针应具有彼此相同的表示和对齐要求。指向其他类型的指针不需要具有相同的表示或对齐要求。


然而6.3.2.3规定:指向void的指针可以转换为任何不完整或对象类型的指针。任何不完整或对象类型的指针都可以转换为指向void的指针,然后再转换回来;结果应与原始指针相等。 ……这难道不适用于这里吗? - msam
到目前为止,这听起来是正确的,但在我接受之前,我正在等待其他意见。由于void *是最通用的指针类型,void **将具有最严格的对齐方式,因此将其转换为(CbData**)将是一个纯粹的重新解释。我的猜测是这个东西是非法的,但即使在最奇怪的架构上也能工作。 - Adrian Ratnapala
@msam在谈论指针值之间的显式类型转换,而我则是在玩弄这些指针的地址。这就是为什么事情不清楚的原因。 - Adrian Ratnapala

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接