4

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

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

4

2 回答 2

6

void *是一个可以保存任何对象指针类型的指针,包括所有指向结构类型的指针。因此,您可以将任何指向结构类型的指针分配给void *.

但是void *并不能保证指向结构类型的指针具有相同的表示形式,因此您的案例 1 是未定义的行为。

(C11,6.2.5p28)“[...] 指向其他类型的指针不需要具有相同的表示或对齐要求。”

于 2012-07-25T10:02:58.670 回答
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来将 avoid *转换为适当的类型:

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(..))
于 2012-07-26T08:56:54.750 回答