联合作为 C 中函数的参数

Union as an argument to a function in C

我想知道是否可以使用联合作为函数的参数:

假设我有两个结构:

struct complex_attribute{
    struct generic_attribute *sub_attributes[20];
};

struct generic_attribute{
    int current_value;
};

这两个的并集:

union union_attribute{
    struct complex_attribute *complex;
    struct generic_attribute *generic;
};

我想创建一个接受 complex_attribute 或 generic_attribute 的函数:

struct tagged_attribute* prepare_tagged_attribute(int code, union union_attribute *attribute)

但是,当我调用这个函数时

prepare_tagged_attribute(2, pointer_to_complex_structure);

我收到这个错误:

 passing argument 2 of ‘prepare_tagged_attribute’ from incompatible pointer type

所以我认为指向复杂结构的指针不一定是联合类型的指针(这是有道理的)...但是可以这样使用联合吗?

由于 C 中没有多态或重载,我不确定是否有完美的解决方案。

只要您将指针包裹在 union_attribute:

中,您的解决方案就会奏效
union union_attribute argument;
argument.complex = pointer_to_complex_structure;
prepare_tagged_attribute(2, &argument);

你必须做这个包装有点烦人,所以一个选择是引入一个新函数来为你做:

union union_attribute wrap_complex(struct complex_attribute* attr) {
    union union_attribute result;
    result.complex = attr;
    return result;
}

然后,重写您的 prepare_tagged_attribute 函数以通过值而不是指针接收 union

struct tagged_attribute* prepare_tagged_attribute(int code, union union_attribute attribute)

然后,你可以这样说

prepare_tagged_attribute(2, wrap_complex(pointer_to_complex_structure));

它当然不完美,但它可以工作并进行类型检查。

希望对您有所帮助!

当您声明函数采用联合参数时,您必须实际传递一个 联合参数,而不是指针。

旁注:如果使用 gcc,请查看 transparent_union 属性。这对您来说可能很有趣,因为它实际上允许上述用法。但是请注意,您仍然必须表明您传递的是哪种类型的参数(与标准方式一样多。

可能更好的方法是将类型和值信息捆绑在一个结构中,例如:

struct variable_argument {
    enum {
        COMPLEX_ARG, GENERIC_ARG
    } type;
    union union_attribute{
        struct complex_attribute *complex;
        struct generic_attribute *generic;
    };
};

并将指向此类结构的指针传递给函数。注意我使用了一个 anonymous struct field 对于工会(自 C99 起)。这样您就可以参考以下字段:

struct variable_argument s;

... s.complex = ...

但是,如果您更改联合类型,请不要忘记适当地设置 s.type

该结构实际​​上将您的指针重新定位到其他结构之一。但是,您可以将它们直接放在联合中而不是使用指针。这简化了记忆 allocation/freeing.

如果您正在使用 GCC,并且愿意使用它的语言扩展,您可以使用 transparent_union.

完成您想要做的事情

来自https://gcc.gnu.org/onlinedocs/gcc-3.1/gcc/Type-Attributes.html

Transparent unions are designed for library functions that have multiple interfaces for compatibility reasons. For example, suppose the wait function must accept either a value of type int * to comply with Posix, or a value of type union wait * to comply with the 4.1BSD interface. If wait's parameter were void *, wait would accept both kinds of arguments, but it would also accept any other pointer type and this would make argument type checking less useful. Instead, might define the interface as follows:

      typedef union
        {
          int *__ip;
          union wait *__up;
        } wait_status_ptr_t __attribute__ ((__transparent_union__));

      pid_t wait (wait_status_ptr_t);

在您的示例中,您可以这样声明 union_attribute

typedef union {
    struct complex_attribute *complex;
    struct generic_attribute *generic;
} union_attribute __attribute__ ((__transparent_union__));

然后您可以按照您提议的方式打电话给 prepare_tagged_attribute

struct tagged_attribute* prepare_tagged_attribute(int code, union_attribute attribute)
{
   attribute.generic_attribute->current_value = 1; // e.g.
   return NULL; // e.g.
}

prepare_tagged_attribute(code, pointer_to_complex_structure);

根本不要使用联合。由于您还传递了指示正在使用哪种指针的代码,因此使用指向 void 的指针并仅传递结构指针:

prepare_tagged_attribute(2, pointer_to_complex_structure);

然后根据代码动态转换你的指针:

if( code == 2 ){
    struct complex_attribute* p = attribute ;
}

您不能(可移植地)将 complex_attribute* 转换为 union_attribute*。但是,如果您使用的是 C99,您可以使用所谓的 "compound literal" 来有效地创建一个未命名的联合,初始化其中的一个成员,获取它的地址,并将它传递给您的函数,所有这些都是在线的。 None 的其他答案似乎提到了这一点 - 这是一个演示该方法的工作程序:

#include <stdio.h>

struct generic_attribute{
    int current_value;
};

struct complex_attribute{
    struct generic_attribute *sub_attributes[20];
};

union union_attribute{
    struct complex_attribute *complex;
    struct generic_attribute *generic;
};

struct tagged_attribute* prepare_tagged_attribute(int code,
                                                  union union_attribute *attribute)
{
    if (code == 1) {
        printf("in func, generic_attribute is %p\n", attribute->generic);
    } else {
        printf("in func, complex_attribute is %p\n", attribute->complex);
    }
    return NULL;
}

int main()
{
    struct generic_attribute g;
    printf("in main, generic_attribute is %p\n", &g);
    prepare_tagged_attribute(1, &(union union_attribute){.generic=&g});

    struct complex_attribute c;
    printf("in main, complex_attribute is %p\n", &c);
    prepare_tagged_attribute(2, &(union union_attribute){.complex=&c});

    return 0;
}

有关此技术的详细信息,请参阅 the section on Compound Literals in the standard。它从 C99 开始可用,但在 C++ 中不可用,许多 C 程序员似乎不知道它。

我只想声明函数接受两种参数类型

prepare_tagged_attribute(struct complex_attribute *complex,struct generic_attribute *generic)

处理第一个非 NULL 参数。