这段代码中union的意义是什么,structure的缺点是什么?

What is the significance of union in this code, what is the disadvantage if structure?

struct queue_entry_s {

    odp_buffer_hdr_t *head;
    odp_buffer_hdr_t *tail;
    int               status;

    enq_func_t       enqueue ODP_ALIGNED_CACHE;
    deq_func_t       dequeue;
    enq_multi_func_t enqueue_multi;
    deq_multi_func_t dequeue_multi;

    odp_queue_t       handle;
    odp_buffer_t      sched_buf;
    odp_queue_type_t  type;
    odp_queue_param_t param;
    odp_pktio_t       pktin;
    odp_pktio_t       pktout;
    char              name[ODP_QUEUE_NAME_LEN];
};

typedef union queue_entry_u {
    struct queue_entry_s s;
    uint8_t pad[ODP_CACHE_LINE_SIZE_ROUNDUP(sizeof(struct queue_entry_s))];
} queue_entry_t;

typedef struct queue_table_t {
    queue_entry_t  queue[ODP_CONFIG_QUEUES];                                                                                           
} queue_table_t;

static queue_table_t *queue_tbl;

#define ODP_CACHE_LINE_SIZE 64

#define ODP_ALIGN_ROUNDUP(x, align)\                                                                                                   
    ((align) * (((x) + align - 1) / (align)))

#define ODP_CACHE_LINE_SIZE_ROUNDUP(x)\
    ODP_ALIGN_ROUNDUP(x, ODP_CACHE_LINE_SIZE)

上面代码中,typedef union queue_entry_u,union有什么意义。如果我们取structure(typedef struct queue_entry_u),有什么缺点吗?

unions有几种用法:

  • union 节省一些内存。它使得 spad 位于内存中的同一个位置。如果您知道只需要其中一个,那么您可以使用 union,这很有用。
  • 能够遍历结构中的字段也很有用。通过将字段保存在联合中,您同时拥有一个数组和一个结构,因此如果您遍历 pad,您实际上是在遍历 s 的字节。
  • unions 通常也可用于铸造。仅使用 union 将您的条目序列化为字节数组的语法更漂亮一些。
  • 在这种情况下,使用 union 似乎是为了填充 s 的大小以适应高速缓存行。这样,如果 queue_entry_s 的大小恰好是缓存行 s 长度的倍数,那么 pad 将位于完全相同的内存中,而不会浪费 space。否则 pad 将比 s 占用更多内存,并且 union 的大小将始终是缓存行长度的精确倍数。

也就是说,如果您正在为内存非常低或性能要求非常严格的设备编写嵌入式代码,那么使用 unions 通常是个好主意。它们非常危险并且很容易被误用,因为意外地覆盖了本应代表 union 中的另一种类型的内存。

让我们从 K&R 第 2 版中联合的定义开始:

A union is a variable that may hold (at different times) objects of different types [...]. Unions provide a way to manipulate different kinds of data in a single area of storage.

问题中的联合包含两个对象:struct queue_entry_s类型的结构和uint8_t类型的数组。重要的是要注意这两个对象在内存中重叠。具体来说,结构开始的地址与数组开始的地址相同。如果写入结构体,数组的内容就会改变,如果写入数组,那么结构体的内容就会改变。

然后注意 ODP_CACHE_LINE_SIZE_ROUNDUP 宏取一个大小并计算大于或等于该大小的 64 的最小倍数。

联合体的大小由最大成员的大小决定。因此,例如,如果 sizeof(struct queue_entry_s) 为 80,则 pad 数组的 sizeof 将为 128,并且 union 的 sizeof 将为 128。

这终于给我们带来了答案。 union的目的是增加结构体使用的内存,使结构体总是使用64字节内存的倍数。

如果您将 typedef union queue_entry_u 更改为 typedef struct queue_entry_u,那么内存布局将发生变化。 spad 在内存中重叠,pad 数组将遵循内存中的 s 结构。所以如果 s 占用 80 个字节, pad 占用 128 个字节,那么 typedef struct queue_entry_u 将定义一个占用 208 个字节内存的对象。那会浪费内存,并且不符合 64 的倍数要求。