void *指针和指向某些结构(布局 - )的指针是否兼容?

时间:2012-07-25 09:58:35

标签: c++ c

换句话说,我可以重新解释(不转换!)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(..)

但是如果没有重新解释指针我找不到任何解决方案(可能我错过了一些可能性)

2 个答案:

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