将包含联合体的结构体作为函数参数传递

编程语言 2026-07-09

在使用包含联合体的结构体时,我遇到了一个小问题。我的结构体成员在函数中无法被识别。我在Linux的 IPv6网络驱动中使用了一个结构体。程序代码:

#include <stdint.h>

typedef struct In6_addr
  {
    union
      {
          uint8_t u6_addr8[16];
          uint16_t u6_addr16[8];
          uint32_t u6_addr32[4];
      } in6_u;
  };

typedef struct Ip6_hdr {
    union {
        struct ip6_hdrctl {
            uint32_t ip6_un1_flow;   /* 4 bits version, 8 bits TC, 20 bits flow-ID */
            uint16_t ip6_un1_plen;   /* payload length */
            uint8_t  ip6_un1_nxt;    /* next header */
            uint8_t  ip6_un1_hlim;   /* hop limit */
        } ip6_un1;
        uint8_t ip6_un2_vfc;         /* 4 bits version, top 4 bits tclass */
    } ip6_ctlun;
    struct In6_addr ip6_src;         /* source address */
    struct In6_addr ip6_dst;         /* destination address */
    void (*set6)(struct Ip6_hdr*, union ip6_ctlun*, struct ip6_hdrctl*,  uint32_t , uint16_t, uint8_t, uint8_t, uint8_t, uint16_t,uint32_t, uint8_t, uint16_t, uint32_t);
} __packed;

//corrected Function
void set6(Ip6_hdr* k, ip6_ctlun*, ip6_hdrctl*, uint32_t  ip6_un1_flow, uint16_t ip6_un1_plen, uint8_t  ip6_un1_nxt, uint8_t ip6_un1_hlim, uint8_t ip6_un2_vfc, uint8_t u6_addr8, uint16_t  u6_addr16, uint32_t u6_addr32) {
    k->ip6_un1_flow = ip6_un1_flow;
    k->ip6_un1_plen = ip6_un1_plen;
    k->ip6_un1_nxt = ip6_un1_nxt;
    k->ip6_un1_nxt = ip6_un1_nxt;
}

void constructIphdr6(Ip6_hdr* k) {
    k->ip6_un1_flow = 0;
    k->ip6_un1_plen = 0;
    k->ip6_un1_nxt = 0;
    k->ip6_un1_nxt = 0;
    k->set6 = set6;
}

int main() {

    Ip6_hdr k;
    constructIphdr6(&k);
}

我已经修正了这个函数(参数错误)

将一个函数指针和一个不包含联合的结构体一起传递时可以正常工作。我没有在函数指针上遇到错误,只有我的结构体成员没有被识别,也无法对它们进行初始化。

错误信息:

error: no member named 'ip6_un1_flow' in 'Ip6_hdr'
    k->ip6_un1_flow = ip6_un1_flow;

error: no member named 'ip6_un1_flow' in 'Ip6_hdr'
    k->ip6_un1_flow = 0;

解决方案

我的结构体成员不能被识别,也不能对它们进行初始化。

显然你的意思是(部分地)你不能用形如 outer_struct_ptr->inner_struct_name 的表达式来引用联合成员中的成员,省略外部结构体中拥有该成员的 ip6_ctlun 名称,也就是它的成员 inner_struct_name。不清楚你为什么认为应该能够这样做(但见下文)。你的 struct Ip6_hdr 类型有以下成员:

  • ip6_ctlun(无标签的联合类型)
  • ip6_srcstruct In6_addr
  • ip6_dststruct In6_addr
  • set6(一个函数指针)

这些是在你对 k-> 操作右边有效的名称,因此要访问 ip6_ctlun 成员的成员,你应当使用诸如 k->ip6_ctlun.ip6_un1 的左值。

同样地,属于 struct 类型的成员联合中的成员也需要指定它的名称才能遍历以访问它们的成员。总之,与其使用 k->ip6_un1_flow,你应该寻找如下形式:

   k           ->  ip6_ctlun         . ip6_un1             . ip6_un1_flow
// an Ip6_hdr*
//                 an untagged union
//                                     a struct ip6_hdrctl 
//                                                           a uint32_t

话虽如此,C语言确实提供了一种方式,使 structunion 类型能够提供匿名的嵌套 structs和 unions,你可以直接从顶层访问它们的成员。也许这正是让你困惑的原因。匿名的 structs和 unions不仅不能有成员名(这也是它们被称为“匿名”的原因),而且它们的类型说明符必须是未标记的 structunion 说明符。假设你不再依赖 struct Ip6_hdr 内部声明的 struct ip6_hdrctl 类型用于其他用途,你可以把类型声明转换成与你尝试的用法相匹配,如下所示:

struct Ip6_hdr {
    union /* never was a tag here */ {
        struct /* no tag here */ {
            uint32_t ip6_un1_flow;   /* 4 bits version, 8 bits TC, 20 bits flow-ID */
            uint16_t ip6_un1_plen;   /* payload length */
            uint8_t  ip6_un1_nxt;    /* next header */
            uint8_t  ip6_un1_hlim;   /* hop limit */
        } /* no member name here */;
        uint8_t ip6_un2_vfc;         /* 4 bits version, top 4 bits tclass */
    } /* no member name here */;
    struct In6_addr ip6_src;         /* source address */
    struct In6_addr ip6_dst;         /* destination address */
    void (*set6)(struct Ip6_hdr*, union ip6_ctlun*, struct ip6_hdrctl*,  uint32_t , uint16_t, uint8_t, uint8_t, uint8_t, uint16_t,uint32_t, uint8_t, uint16_t, uint32_t);
};

这样,你确实可以写成 k->ip6_un1_flow,其中 k 的类型是 struct Ip6_hdr *

请注意,原始声明中的 typedef__packed 结合起来,确实把 __packed 声明为 struct Ip6_hdr 的别名。至少如果 __packed 不是保留标识符的话会是这样。我倾向于猜测 __packed 本来是作为一个扩展关键字,用来指定结构打包,但如果编译器这样解释,那么 typedef 的声明就会因为没有声明任何标识符而出错。

我的个人做法与建议是把 typedef 只保留给能够显著提升代码可读性的场景。我很少对结构体和联合体类型使用 typedef,但函数类型则另当别论。举例来说,我更倾向于使用上面这一替代版本:

struct Ip6_hdr;  // forward declaration
typedef void set6_func(struct Ip6_hdr*, union ip6_ctlun*, struct ip6_hdrctl*, uint32_t,
        uint16_t, uint8_t, uint8_t, uint8_t, uint16_t,uint32_t, uint8_t, uint16_t, uint32_t);

struct Ip6_hdr {
    union /* never was a tag here */ {
        struct /* no tag here */ {
            uint32_t ip6_un1_flow;   /* 4 bits version, 8 bits TC, 20 bits flow-ID */
            uint16_t ip6_un1_plen;   /* payload length */
            uint8_t  ip6_un1_nxt;    /* next header */
            uint8_t  ip6_un1_hlim;   /* hop limit */
        } /* no member name here */;
        uint8_t ip6_un2_vfc;         /* 4 bits version, top 4 bits tclass */
    } /* no member name here */;
    struct In6_addr ip6_src;         /* source address */
    struct In6_addr ip6_dst;         /* destination address */
    set6_func *set6;
};

如果将 set6_func 类型改名为更有意义的名称,并给它起一个有意义的参数名,效果将更好。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章