总结一下:是的,这在 C 中是有效的,尽管在 C++ 中是非法的。后者包含解释差异的注释
更改:在 C++ 中,类型可能未在返回或参数类型中定义。在 C 中,这些类型定义是允许的
例子:
void f( struct S { int a; } arg ) {} // valid C, invalid C++
enum E { A, B, C } f() {} // valid C, invalid C++
- 理由:在比较不同编译单元中的类型时,C++ 依赖于名称等价,而 C 依赖于结构等价。关于参数类型:由于参数列表中定义的类型将在函数的范围内,因此 C++ 中唯一合法的调用将来自函数本身。
- 对原始特征的影响:删除语义上定义明确的特征。
- 转换难度:语义转换。类型定义必须移动到文件范围或头文件中。
- 使用范围:很少。这种类型定义的风格被视为糟糕的编码风格。
C 中的结构等价是通过“类型兼容性”的概念完成的。这允许 C 将许多类型视为相同,即使它们在理论上是不同的 - 因为它们是在两个不同的翻译单元中声明的。在 C++ 中,这个概念不存在,因为类型具有链接并且匹配到同一个实体(即允许成员函数相互链接)。
请注意,上面引用的解释基于 C89,在确定类型兼容性时没有考虑结构的标记名称。在C89 草案中,相关文本如下:
此外,如果在不同的翻译单元中声明的两个结构、联合或枚举类型具有相同的成员数量、相同的成员名称和兼容的成员类型,则它们是兼容的;对于两个结构,成员的顺序应相同;
在 C99 中,类型检查更加严格:如果一个结构具有标记名称,则另一个结构声明必须具有相同的标记名称。因此,在您的未命名联合类型的情况下,要在另一个具有兼容类型的 TU 中声明一个函数,如果您想拥有有效的 C99 代码(没有未定义的行为),您将再次需要一个未命名的联合 - 您不能“欺骗”周围,并且在一个 TU 中使用命名联合,在另一个 TU 中使用未命名联合。不过,在我看来,这个“技巧”对 C89 有效。C99 TC3 6.2.7/1
:
此外,如果它们的标记和成员满足以下要求,则在单独的翻译单元中声明的两种结构、联合或枚举类型是兼容的:如果用标记声明一个,则另一个应用相同的标记声明。如果两者都是完整类型,则适用以下附加要求:它们的成员之间应存在一对一的对应关系,使得每对对应的成员都声明为兼容的类型,并且如果对应对的一个成员是用一个名字声明,另一个成员用相同的名字声明。对于两个结构,相应的成员应以相同的顺序声明。
你想要的方式是行不通的。调用函数会将参数转换为参数的类型,就像通过正常赋值一样。
因此,要使其正常工作,您必须有一个与参数类型兼容的参数。对于在同一个翻译单元中声明的两个联合,这意味着它们的类型必须相等 - 这是您在同一个翻译单元中提出兼容类型的唯一方法。但这不起作用,因为未命名联合的声明创建了一个唯一的新类型 - 无法使用另一个声明“引用”它。
所以,总结一下——你必须给联合类型一个名字。为了避免创建一个单独的变量来传递所需的基本参数,我会在函数之外声明它,并创建返回一个你可能会传递的联合的函数
union base_type {
uint16_t b16;
uint32_t b32;
uint64_t b64;
};
int pcg_new_state(pcg_state *s,int arch,void *mem,int sz,
union base_type base,int self_running);
union base_type base_b16(uint16_t t)
{ union base_type b; b.b16 = t; return b; }
union base_type base_b32(uint32_t t)
{ union base_type b; b.b32 = t; return b; }
union base_type base_b64(uint64_t t)
{ union base_type b; b.b64 = t; return b; }
现在,它可能如下所示
pcg_new_state(...., base_b32(4211), ....);