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(..))