换句话说,我可以重新解释(不转换!)void*
指针作为指向某种结构类型的指针(假设void*
指针确实保存了正确转换的有效结构地址)
实际上我对以下场景感兴趣:
typedef struct void_struct void_struct_t;
typedef somestruct
{
int member;
// ... other members ...
}somestruct_t;
union
{
void* pv;
void_struct_t* pvs;
somestruct_t* ps;
}u;
somestruct_t s={};
u.pv= &s;
u.ps->member=1; // (Case 1) Ok? unspecified? UB?
u.pvs=(void_struct_t*)&s;
u.ps->member=1; // (Case 2) )Ok?
我在C11标准中发现的内容对于案例1来说相当令人失望:
§6.2.5
28指向void的指针应具有与指向字符类型的指针相同的表示和对齐要求。[脚注:相同的表示和对齐要求>意味着可互换性作为函数的参数,返回值来自>函数和联合成员。]类似地,指向兼容类型的限定或不合格>版本的指针应具有相同的表示和对齐>要求。所有指向结构类型的指针都应具有相同的表示和对齐要求。所有指向联合类型的指针都应具有相同的>表示和对齐要求。指向其他类型的指针不需要具有相同的表示或对齐要求。
但是,似乎案例2是有效的,但我并非100%确定......
这个问题主要是面向C的,但我对C ++也感兴趣(我希望代码在C ++编译器编译时有效)。老实说,我在C ++ 11标准中发现更少,所以即使案例2对我来说也是值得怀疑的......但是,我可能会遗漏一些东西。
[编辑] 这个问题背后的真正问题是什么?
我有一组(可能很大)定义为结构的类型。 对于每种类型,我需要定义一个伴随类型:
typedef struct companion_for_sometype
{
sometype* p_object;
// there are also other members
}companion_for_sometype;
显然,伴随类型将是C ++中的模板,但我需要一个C的解决方案 (更准确地说,对于“干净的C”,即C89和C ++的交集,因为我希望我的代码也是有效的C ++代码)。
幸运的是,即使在C中也不是问题,因为我可以定义一个宏
DECLARE_COMPANION(type_name) typedef struct companion_for_##type_name
{
type_name* p_object;
// there are also other members
}companion_for_##type_name;
并为每个需要伴侣的类型调用它。
对伴随类型还有一组通用操作。 这些操作也由宏定义(因为纯C中没有重载)。
其中一项操作,比如说
#define op(companion_type_object) blablabla
应该为伴随对象的void*
字段指定一个p_object
指针,
即应该做这样的事情:
(companion_type_object).p_object= (type_name*) some_function_returning_pvoid(..)
但宏不知道type_name(只有伴随类型的对象传递给宏) 所以宏不能做适当的指针转换。
问题实际上是受这个问题的启发。
为了解决这个问题,我决定将赋值中的目标指针重新解释为void *,然后赋值给它。 可以通过用指针联合替换伴随声明中的指针来完成 (问题是关于这种情况),或者可以直接重新解释目标指针,例如:
*(void**) &(companion_type_object).p_object= some_function_returning_pvoid(..)
但是如果没有重新解释指针我找不到任何解决方案(可能我错过了一些可能性)
答案 0 :(得分:5)
void *
是一个指针,可以保存任何对象指针类型,包括所有指向结构类型的指针。因此,您可以将任何指向结构类型的指针分配给void *
。
但是void *
和结构类型的指针不能保证具有相同的表示,因此您的情况1是未定义的行为。
(C11,6.2.5p28)“[...]指向其他类型的指针不必相同 表示或对齐要求。“
答案 1 :(得分:2)
在C中,void *
会自动转换为任何对象类型,因此这将起作用:
(companion_type_object).p_object = some_function_returning_pvoid(..)
在C ++中,您需要使用static_cast
,但您可以使用decltype
找到所需的类型:
(companion_type_object).p_object =
static_cast<decltype(*(companion_type_object).p_object) *>(
some_function_returning_pvoid(..))
在C ++ 03中,你应该可以使用一些等同于decltype
的编译器扩展。或者,您可以在companion_type_object
上提供一个宏生成的方法,以将void *
转换为适当的类型:
static type_name *void_p_to_object_p(void *p) { return static_cast<type_name *>(p); }
...
(companion_type_object).p_object = companion_type_object.void_p_to_object_p(
some_function_returning_pvoid(..))