【问题标题】:Union as an argument to a function in C联合作为 C 中函数的参数
【发布时间】:2015-06-26 18:27:53
【问题描述】:

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

假设我有两个结构:

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


    【解决方案1】:

    如果您使用 GCC,并且愿意使用其语言扩展,您可以使用 transparent_union 完成您想要做的事情。

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

    透明联合是为具有 出于兼容性原因的多个接口。例如,假设 等待函数必须接受 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);
    

    【讨论】:

      【解决方案2】:

      由于 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 属性。这对您来说可能很有趣,因为它实际上允许上述用法。请注意,但是,您仍然需要表明您传递的是哪种类型的参数(与标准方式一样多。

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

        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。

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

        【讨论】:

          【解决方案4】:

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

          prepare_tagged_attribute(2, pointer_to_complex_structure);
          

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

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

          【讨论】:

          • 虽然这当然是一种选择,但它会丢失类型安全信息。您可以将指向 anything 的指针传递给采用void* 的函数,这意味着您可以传递int*、char** 等,即使您不应该这样做。我认为 OP 的想法是让它很难出错。
          • @templatetypedef 我实际上赞成你的回答,但想展示这种方法。
          【解决方案5】:

          您不能(可移植地)将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 程序员似乎都不知道。

          【讨论】:

          • 再次仔细阅读其他答案。至少有人提到过。
          • 哦,你的意思不是匿名结构字段(或联合字段,而是复合字面量。请获取条款正确。
          • @Olaf 我不这么认为- gubblebozer 的答案是通过另一个函数间接而不是使用我引用的 C99 功能,并且您的答案将未命名的联合引用为结构的成员,但不是未命名的在函数调用中创建和初始化联合 - 这也是一个不同的功能。
          • @Olaf 很公平,我不记得该功能的名称,并且在不记得名称的情况下使用谷歌搜索它时运气不佳。我会编辑它 - 谢谢!
          • @Olaf 引用标准-“由带括号的类型名称后跟用大括号括起来的初始化程序列表组成的后缀表达式是复合文字。它提供了一个未命名的对象,其值由下式给出初始化列表。” - 称它为“未命名的工会”并不是完全。 :-) 我也会添加到标准部分的链接。
          【解决方案6】:

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

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

          处理第一个非 NULL 参数。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-10-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-07-03
            • 1970-01-01
            相关资源
            最近更新 更多