C 在函数参数中使用并集

C 在函数参数中使用并集,c,function,types,parameters,unions,C,Function,Types,Parameters,Unions,我最近发现有些东西可以编译(但不确定它是否合法)。我对这样一个东西的需求来自于:我的项目为所选的arch输出机器代码(它可能与运行程序的arch相同,也可能不同)。因此,我现在想支持多达64位的体系结构(同时也支持现有的32位和16位ARCH)。我当前的解决方案是,新州的“基础”只是一个uint64,并根据需要手动转换为16位和32位。不过,我发现可以在函数参数中编译联合。因此,在编译时,函数: int pcg_new_state(pcg_state *s,int arch,void *mem,

我最近发现有些东西可以编译(但不确定它是否合法)。我对这样一个东西的需求来自于:我的项目为所选的arch输出机器代码(它可能与运行程序的arch相同,也可能不同)。因此,我现在想支持多达64位的体系结构(同时也支持现有的32位和16位ARCH)。我当前的解决方案是,新州的“基础”只是一个uint64,并根据需要手动转换为16位和32位。不过,我发现可以在函数参数中编译联合。因此,在编译时,函数:

int pcg_new_state(pcg_state *s,int arch,void *mem,int sz,
             union{
    uint16_t b16;
    uint32_t b32;
    uint64_t b64;
}base ,int self_running);

然而,这种事情是“合法”的还是得到其他编译器的支持?而且,如果不创建一个联合,然后将该联合传递到新的_状态,我就无法理解如何调用该函数。

我快速浏览了(编辑:C++)标准,但在8.3.5-Functions[dcl.fct]或9.5-Unions[class.union]中都没有看到任何禁止它的内容。作为一个半受过教育的猜测,我认为通过一个联盟是合法的,但不能像那样宣布一个联盟。GCC给出:

错误:参数类型中可能未定义类型

因此,您必须提前定义类型

然而,即使这是合法的,也不意味着这是个好主意。只要看一看,我建议过载可能会提供更好的解决方案。但是你当然最了解你的代码。。。只是想寻找一个更简单、更惯用的解决方案。

这个怎么样:

typedef struct _base
{
    union
    {
        uint16_t b16;
        uint32_t b32;
        uint64_t b64;
    };
}Base;

int func(Base b)
{
    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{

    Base b;
    b.b16 = 0xFFFF;
    func(b);

    return 0;

}

通过在函数中为union提供一个名称,您可以在GCC中编译它,但由于定义的范围,您将无法使用它,正如GCC警告的那样:

test_union.c:14: warning: ‘union X’ declared inside parameter list
test_union.c:14: warning: its scope is only this definition or declaration, which is probably not what you want
代码:


总结:是的,在C++中是有效的,虽然在C++中是非法的。后者包含解释差异的注释

< > >强> >:<强> > C++中,类型不可定义为返回或参数类型。在C中,这些类型定义是允许的

例如:

    <>强>理论:当不同编译单元中的类型比较时,C++依赖于C等价依赖于结构等价的名称等价。关于参数类型:因为在参数列表中定义的类型将在函数的范围内,C++中唯一合法的调用将来自函数本身。
  • 对原始特征的影响:删除语义上定义良好的特征
  • 转换难度:语义转换。类型定义必须移动到文件范围或头文件中
  • 使用范围有多广:很少。这种类型定义的样式被视为糟糕的编码样式
C中的结构等价是通过“类型兼容性”的概念来实现的。这允许C将许多类型视为相同的,即使它们在理论上是不同的——因为它们是在两个不同的翻译单元中声明的。在C++中,这个概念不存在,因为类型有链接,并且与同一个实体相匹配(即允许成员函数相互链接)。p>

注意,上面引用的解释是基于C89的,在确定类型兼容性时没有考虑结构的标签名。在a部分,相关案文如下:

此外,如果在单独的转换单元中声明的两个结构、联合或枚举类型具有相同的成员数量、相同的成员名称和兼容的成员类型,则它们是兼容的;对于两个结构,构件的顺序应相同

在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), ....);

不管怎样,工会的规模将是最大会员的规模——因此你不会得到任何好处。您也可以只使用参数uint64_t,因为从较大的无符号类型到较小的无符号类型的转换是定义良好的,通常实现起来比较便宜。(例如指派
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), ....);
int pcg_new_state(pcg_state *s,int arch,void *mem,int sz, uint64_t param_base ,int self_running)
{
    /* Implementation for 16 bit arch */
    uint16_t base = param_base;

    /* ... more code ... */
}