填充结构的正确(现代)方法是什么?

时间:2011-11-12 05:33:44

标签: c debugging

编辑:一种更好的表达方式:确保结构是一个特定大小的字节的正确[现代]方法是什么?

只是在周六下午放松一下调试一个legacy代码库,并且在解决这个问题时遇到了一些麻烦。我得到的编译器错误是:

INC/flx.h:33: error: dereferencing pointer to incomplete type

第33行的代码看起来像这样

typedef struct flx_head {
FHEAD_COMMON;
LONG frames_in_table; /* size of index */
LONG index_oset;    /* offset to index */
LONG path_oset;     /* offset to flipath record chunk */

/* this will insure that a Flx_head is the same size as a fli_head but won't
 * work if there is < 2 bytes left (value <= 0) */

PADTO(sizeof(Fli_head),flx_head,flxpad);  /* line 33 is this one */
} Flx_head;

好吧,所以我可以看到结构是指自己以某种方式填充它。但是我不知道在没有自我引用的情况下做PADTO的另一种方法。

这是PADTO被定义为

的内容
 #define MEMBER(struc,field) \
    ((struc*)NULL)->field

/* returns offset of field within a given struct name,
 * and field name ie: OFFSET(struct sname,fieldname) */

#define OFFSET(struc,field) \
    (USHORT)((ULONG)((PTR)&MEMBER(struc,field)-(PTR)NULL))

/* offset to first byte after a field */

#define POSTOSET(struc,field) \
    (OFFSET(struc,field)+sizeof(MEMBER(struc,field)))

/* macro for defining pad sizes in structures can not define a pad of 
 * less than two bytes  one may use pname for the offset to it but
 * sizeof(struc->pname) will not be valid 
 *
 *  struct sname {
 *      char fld1[64];
 *      PADTO(68,sname,pname);
 *  };
 * will make:
 *
 *  struct sname {
 *      char fld1[64];
 *      UBYTE pname[1];
 *      UBYTE __pname[3];
 *  };
 */


#define PADTO(sz,struc,padfld) \
    UBYTE padfld[1];UBYTE __##padfld[(sz)-OFFSET(struct struc,padfld)-1]

这里是FHEAD_COMMON

#define FHEAD_COMMON \
CHUNKID_FIELDS;\
USHORT frame_count;\
USHORT width;\
USHORT height;\
USHORT bits_a_pixel;\
SHORT flags;\
LONG speed;\
USHORT unused;\
Fli_id id;\
USHORT aspect_dx;\
USHORT aspect_dy;\
UBYTE commonpad[38]  /* should be total of 80 bytes (48 for unique) */

和flihead

typedef struct fli_head {
FHEAD_COMMON;
LONG frame1_oset;
LONG frame2_oset;
UBYTE padfill[40];
} Fli_head;

这是Autodesk animator pro。我正在研究的是FLI文件格式的“参考”实现 - 你可以在这里看到一个规范:

http://www.compuphase.com/flic.htm

顺便说一句,我很确定/源代码/那里所谓的“flx”实际上是那个网页所谓的“flc”,而不是它所谓的“flx” 更新: 更好的格式信息来源http://drdobbs.com/architecture-and-design/184408954

5 个答案:

答案 0 :(得分:3)

它不漂亮,但一种可能性是定义另一个相同的结构并使用其大小来确定您实际想要使用的填充的填充:

#define FLX_HEAD \
FHEAD_COMMON;\
LONG frames_in_table; /* size of index */ \
LONG index_oset;    /* offset to index */ \
LONG path_oset     /* offset to flipath record chunk */

struct flx_head_unpadded {
  FLX_HEAD;
};

typedef struct flx_head {
  FLX_HEAD;
  char __flxpad[sizeof(Fli_head)-sizeof(struct flx_head_unpadded)];
} Flx_head;

答案 1 :(得分:1)

我认为答案取决于你想要达到的目标。在大多数情况下,填充结构的正确,现代方法是。我可以想到填充结构的合法地方的唯一情况是当你有一个库接口,其中调用者创建结构类型的对象并将指针传递给库,并且你想留出空间来添加其他字段在不破坏ABI的情况下进入结构。在这种情况下,我会从char pad[256];开始,并在添加字段时将其更改为char pad[256-3*sizeof(long)];或类似内容(确保在添加字段时避免内部填充)。

答案 2 :(得分:1)

在一个带有所需大小的byte / char数组的联合中定义它?

我可以快速思考一些需要这种情况的场景:

1)与使用平面二进制文件存储数据的旧软件兼容(如在OP中)。

2)与驱动程序和/或硬件的交互

3)强制结构化为高速缓存行大小的精确倍数,以防止线程间通信中的错误共享。

答案 3 :(得分:0)

如果你只想达到特定的尺寸,你可以使用(肯定在GCC工作):

typedef union {
struct {
    FHEAD_COMMON;
    LONG frames_in_table; /* size of index */
    LONG index_oset;    /* offset to index */
    LONG path_oset;     /* offset to flipath record chunk */
};
uint8_t __padding[128];
} Flx_head;

void test() {
    Flx_head boo;
    boo.frames_in_table= 0;
}

我不确定这是否足够现代。如果你的编译器不支持匿名结构(联盟内部),它将变得“混乱”。

此外,您必须记住结构现在已填充,但未与特定数据大小对齐。

答案 4 :(得分:-1)

谢谢大家。不知道是谁给绿色复选标记,因为我发现这个解决方案是由于每个人的暗示和指向正确的方向。在查看问题之后,让我感到震惊的是结构只需要128个字节。我“手工解析”宏,交叉引用规范并最终得到:

typedef struct flx_head {
FHEAD_COMMON;
LONG frames_in_table; /* size of index */
LONG index_oset;    /* offset to index */
LONG path_oset;     /* offset to flipath record chunk */

    UBYTE flxpad[36];
} Flx_head;

是128-(80 + 4 + 4 + 4)= 36