使用pack控制struct内存对齐

最近又踩一坑,一个结构体在模块A和模块B中被定义,模块A依赖于模块B,由于B模块中结构体定义于内部,对外不可见, 编译不会出现重复定义的错误。两处定义一模一样,估计是模块A需要使用到该结构体的定义,但该结构体在模块B原来设计是对外不可见的, 同时开发者是模块A的维护者,却不是模块B的维护者,然后就做了最简单的处理:直接将该结构体的定义拷贝到了模块A中。 如此一个坑就挖好了。

由于大伙写代码,防御性代码写得特别勤快,连unsigned int类型的变量都要跟0比较一下大小,目的是防止哪天unsigned int被改成了int,而忘记了判断是否大于0。这样带来的结果是,程序不core了,而且哪怕程序或者数据已经出问题了,也不会core了, 如果日志打得不多,或者运维对日志的监控不够,可能会导致问题被隐藏几周都不被发现。因为向来程序员对core都是胆战心惊,兢兢业业,快速响应,而只要不出core,就以为万事大吉,而高枕无忧。

对头,我遇到的这个问题就是,程序运行良好,日志正常,但就是输出结果不是想要的,追来追去,发现是一个结构体在两处定义,尽管看似定义完全一样,但实际一个是4字节对齐,一个是8字节对齐,从而导致一个指向该结构体的指针在移动时,就移飞了。

梳理一下使用pack控制struct字节对齐的方式。

最不推荐的就是使用gcc/g++编译参数,格式如下:

-fpack-struct[=n]

因为这会导致出现问题很难发现,而且如果编译文件include了其他文件,被include的文件,也会被加以同等对齐方式,很可能导致编译出来的二进制不兼容,或者行为诡异。

推荐使用__attribute__ ((packed))方式,意思就是紧密型对齐。再或者使用#pragma pack()方式,这是微软风格的宏控制方式, gcc也支持。例子代码如下:

typedef struct {
    uint64_t    field_type:16;
    uint64_t    sign_count:16;
    uint64_t    info_offset:32;
    uint64_t    field_attr_len:12;
    uint64_t    field_attr_off:20;
}extkv_field_info_t0;

typedef struct {
    uint32_t    field_type:16;
    uint32_t    sign_count:16;
    uint32_t    info_offset:32;
    uint32_t    field_attr_len:12;
    uint32_t    field_attr_off:20;
}extkv_field_info_t1;

#pragma pack(1)
typedef struct {
    uint64_t    field_type:16;
    uint64_t    sign_count:16;
    uint64_t    info_offset:32;
    uint64_t    field_attr_len:12;
    uint64_t    field_attr_off:20;
}extkv_field_info_t2;
#pragma pack(0)

typedef struct {
    uint64_t    field_type:16;
    uint64_t    sign_count:16;
    uint64_t    info_offset:32;
    uint64_t    field_attr_len:12;
    uint64_t    field_attr_off:20;
} __attribute__ ((packed)) extkv_field_info_t3;

上面各结构体的大小依次如下:

16
12
12
12

注意,extkv_field_info_t0默认是8字节对齐,而extkv_field_info_t1是4字节,#pragma pack(1)表示使用1字节对齐, 但真正的对齐是由结构体中最小的元素决定的,#pragma pack(0)相当于关闭pack对齐,其中参数0可以不写。

#pragma pack()的一开一关,使其可以控制一块区域内的struct的对齐方式,而如果像下面这么写,一个隐蔽性强的坑就产生了:

#pragma pack(1)
#include "common.h"
#pragma pack(0)

common.h里的结构体定义都采用pack对齐,而阅读common.h代码本身,你很难发现这点。

发表于 2014年06月06日 17:22   评论:0   阅读:2851  



回到顶部

首页 | 关于我 | 关于本站 | 站内留言 | rss
python logo   django logo   tornado logo