联盟作为C中函数的参数

问题描述 投票:4回答:6

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

假设我有两种结构:

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 function structure unions
6个回答
3
投票

由于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));

当然,这并不完美,但它的工作和类型检查。

希望这可以帮助!


3
投票

如果您正在使用GCC,并且愿意使用其语言扩展,那么您可以完成使用transparent_union尝试的操作。

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

透明联合是为具有多个接口的库函数而设计的,这是出于兼容性原因。例如,假设wait函数必须接受int *类型的值以符合Posix,或者类型为union wait *的值以符合4.1BSD接口。如果wait的参数是void *,那么wait会接受这两种参数,但它也会接受任何其他指针类型,这会使参数类型检查不那么有用。相反,可以按如下方式定义接口:

      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);

1
投票

你不能(便携)将complex_attribute*施放到union_attribute*。但是,如果您正在使用C99,您可以使用所谓的“复合文字”来有效地创建一个未命名的联合,初始化其中的成员,获取其地址,并将其传递给您的函数。其他答案似乎都没有提到这一点 - 这是一个展示这种方法的工作程序:

#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程序员似乎都不知道它。


1
投票

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

旁注:如果使用gcc,请查看transparent_union属性。这对你来说可能很有趣,因为它实际上允许上面的用法。请注意,但是,您仍然需要发出通过哪种类型的参数的信号(与标准方式一样多)。

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

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

并将指向此类结构的指针传递给函数。注意我对联合使用匿名结构字段(自C99起)。这样你可以参考如下字段:

struct variable_argument s;

... s.complex = ...

但是,如果改变了联合的类型,千万不要忘记适当地设置s.type

这个结构实际上将指针与其他结构之一相关联。但是,您可以直接在联合中使用它们而不是使用指针。这简化了内存分配/释放。


0
投票

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

prepare_tagged_attribute(2, pointer_to_complex_structure);

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

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

0
投票

我只是声明函数采用两种参数类型

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

处理第一个非NULL参数。

© www.soinside.com 2019 - 2024. All rights reserved.