C++ void*指针和指向某些结构(布局)的指针是否兼容?
换句话说,我可以重新解释(不转换!)C++ void*指针和指向某些结构(布局)的指针是否兼容?,c++,c,C++,C,换句话说,我可以重新解释(不转换!)void*指针作为指向某个结构类型的指针(假设void*指针确实包含正确转换的有效结构地址) 实际上,我对以下场景很感兴趣: typedef struct void_struct void_struct_t; typedef somestruct { int member; // ... other members ... }somestruct_t; union { void* pv; void_s
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无效指针的表示和对齐要求应与
指向字符类型的指针。[脚注:相同的表示和对齐要求
意味着作为函数参数的互换性,从
函数和联合的成员。]类似地,指向合格或不合格的指针
兼容类型的版本应具有相同的表示和对齐方式
要求。指向结构类型的所有指针应具有相同的表示形式和
校准要求彼此一致。指向联合类型的所有指针应具有相同的
表示和对齐要求彼此相同。指向其他类型的指针不需要
具有相同的表示或对齐要求
看起来,虽然,案例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_对象
字段,
i、 e.应该这样做:
(companion_type_object).p_object= (type_name*) some_function_returning_pvoid(..)
但是宏不知道type_name(只有一个伴生类型的对象被传递给宏)
因此宏无法执行适当的指针强制转换
这个问题实际上是从这个问题中得到启发的
为了解决这个问题,我决定将赋值中的目标指针重新解释为void*,然后赋值给它。
这可以通过将伴随声明中的指针替换为指针的并集来实现
(问题是关于这种情况),或者可以直接重新解释目标指针,比如:
*(void**) &(companion_type_object).p_object= some_function_returning_pvoid(..)
但是如果不重新解释指针,我就找不到任何解决方案(也许我缺少了一些可能性)
void*
是一个可以容纳任何对象指针类型的指针,其中包括指向结构类型的所有指针。因此,您可以将指向结构类型的任何指针指定给void*
但是void*
和指向结构类型的指针不能保证具有相同的表示形式,因此案例1是未定义的行为
(C11,6.2.5p28)“[…]指向其他类型的指针不必具有相同的
表示或对齐要求。”
在C中,
void*
自动强制转换为任何对象类型,因此这将起作用:
(companion_type_object).p_object = some_function_returning_pvoid(..)
<>在C++中,你需要使用<代码> SistaCysCAP,但是你可以使用<代码> DECKEXT/<代码>:
(companion_type_object).p_object =
static_cast<decltype(*(companion_type_object).p_object) *>(
some_function_returning_pvoid(..))
你认为案例2是有效的吗?什么是代码>结构VoIDyStult?您没有在示例中声明结构类型。它只是对void的替换。。我甚至不想定义它,但如果需要的话,它可能是任意定义的。void_struct_t*只是一个用void*指针绕过可能的UB的工作。它实际上是“指向任何结构的指针”的占位符。我希望它与指向实际结构的指针的并集能够工作……案例2实际上同样没有定义,因为它向一个并集成员写入数据,并从另一个并集成员读取数据。C++标准说:“在一个联合中,最多一个非静态数据成员可以在任何时候都是活动的,也就是说,最多一个非静态数据成员的值可以随时存储在一个联合体中。”您只能读取当前存储在联合中的成员。大多数编译器都有一个允许这种情况的扩展,但该语言没有。尽管它在技术上是UB,但在实践中,情况1很可能适用于任何编译器/系统,除非该结构是多态的/使用继承。@smerlin:我怀疑,即使是指向多态结构的指针,它也很可能适用于大多数编译器,但是..是的,在大多数情况下,但对于使用虚拟多重继承的类/结构,它肯定不起作用。@user396672-您想做什么?也许我们可以回答如何做?C和C++在这些方面确实不同,所以你的C++标签可能是误导的。但是C在从空隙中铸造时发出警告(*)它不应该;您确定
某些返回的函数\u pvoid
确实返回了void*
?谢谢,现在我明白我真正应该做什么了。我需要一个特殊的宏,比如,null pTrpRead(Type p,VoIDyp),并且用不同的方式实现C++和C的这个宏(并且对于一些具有DECKET模拟的C编译器可能不同)。Null pTrpRead赋值(Type p,VoIDyp)可以是C++案例的函数模板(不是宏)(不带DECKEYPE,即使对于C++ 03也是如此)。很抱歉,我不能选择两个被接受的答案(ouah的答案更符合原始问题)。我只能向上投票,尽管你的答案是我真正需要的。@user396672-首先不要使用void。void这个词是一个暗示-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(..))