我想知道是否可以使用联合作为函数的参数:
假设我有两种结构:
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
尝试的操作。
来自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);
你不能(便携)将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程序员似乎都不知道它。
当您声明函数采用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
。
这个结构实际上将指针与其他结构之一相关联。但是,您可以直接在联合中使用它们而不是使用指针。这简化了内存分配/释放。
根本不要使用工会。由于您还传递了指示正在使用何种指针的代码,因此请使用指向void的指针并仅传递结构指针:
prepare_tagged_attribute(2, pointer_to_complex_structure);
然后根据代码动态转换指针:
if( code == 2 ){
struct complex_attribute* p = attribute ;
}
我只是声明函数采用两种参数类型
prepare_tagged_attribute(struct complex_attribute *complex,struct generic_attribute *generic)
处理第一个非NULL参数。