平台独立存储c位字段

时间:2013-07-24 10:37:29

标签: c bit-fields

我使用C位域将数据存储在内存中。对于存档使用,必须将这些数据写入文件(以后再与另一台机器的数据结合)。将位域直接保存到文件似乎是个坏主意,因为数据的排列是特定于实现的。

出于这个原因,我写了一些方法来“序列化”这些位域,以统一格式保存它们:

/* uint16 is a unsigned int with size 16 */

typedef struct {
    uint16 a : 1;
    /* ... just examples ... */
    uint16 z : 13;
} data;

void save_data(FILE* fp, data d) {
    uint16 tmp;
    tmp = d.a;
    fwrite(&tmp,sizeof(uint16),1,fp);
    /* ... */
    tmp = d.z;
    fwrite(&tmp,sizeof(uint16),1,fp);
}

虽然这非常有效,但扩展似乎不太好,因为在data中添加更多成员也需要将数据添加到保存例程中。

在更改位域数据时,是否有任何技巧可以自动将位域数据转换为统一格式而无需调整例程/宏

3 个答案:

答案 0 :(得分:1)

如果您愿意投入一点资金,可以使用P99等工具进行“声明展开”:

// in header
#define MY_BITFIELDS a, z
#define WRITE_IT(X) fwrite(&(unsigned){ d.X }, sizeof(unsigned), 1, fp)    
#define WRITE_ALL(...) P99_SEP(WRITE_IT, __VA_ARGS__)


// in your function
WRITE_ALL(MY_BITFIELDS);
顺便说一下,如果可以避免这种情况,请不要将int用于位域。 unsigned可以更好地匹配一组位的语义。

通过更多的宏编码,您甚至可以使用类似

的内容
#define MY_BITFIELDS (a, 1), (z, 11)

生成struct声明写入部分。

答案 1 :(得分:1)

这是一种方法。我不能推荐它,但是它在那里并且有点有效,所以为什么不看它。这种化身仍然依赖于平台,但您可以轻松切换到与平台无关的,可能是人类可读的格式。为简洁起见,省略了错误处理。

// uglymacro.h
#if defined(DEFINE_STRUCT)

#define BEGINSTRUCT(struct_tag)     typedef struct struct_tag {
#define ENDSTRUCT(struct_typedef)   } struct_typedef;
#define BITFIELD(name,type,bit)     type name : bit;
#define FIELD(name,type)            type name;
#define ARRAYFIELD(name,type,size)  type name[size];

#elif defined(DEFINE_SAVE)

#define BEGINSTRUCT(struct_tag)     void save_##struct_tag(FILE* fp, \
                                                           struct struct_tag* p_a) {
#define ENDSTRUCT(struct_typedef)   }
#define BITFIELD(name,type,bit)     { type tmp; tmp = p_a->name; \
                                      fwrite (&tmp, sizeof(type), 1, fp); }
#define FIELD(name,type)            { fwrite (&p_a->name, sizeof(p_a->name), 1, fp); }
#define ARRAYFIELD(name,type,size)  { fwrite (p_a->name, sizeof(p_a->name[0]), size, fp); }

#elif defined(DEFINE_READ)

#define BEGINSTRUCT(struct_tag)     void read_##struct_tag(FILE* fp, \
                                                           struct struct_tag* p_a) {
#define ENDSTRUCT(struct_typedef)   }
#define BITFIELD(name,type,bit)     { type tmp; fread (&tmp, sizeof(type), 1, fp); \
                                      p_a->name = tmp; }
#define FIELD(name,type)            { fread (&p_a->name, sizeof(p_a->name), 1, fp); }
#define ARRAYFIELD(name,type,size)  { fread (p_a->name, sizeof(p_a->name[0]), size, fp); }

#else
#error "Must define either DEFINE_STRUCT or DEFINE_SAVE or DEFINE_READ"
#endif

#undef DEFINE_STRUCT
#undef DEFINE_READ
#undef DEFINE_WRITE
#undef BEGINSTRUCT
#undef ENDSTRUCT
#undef FIELD
#undef BITFIELD
#undef ARRAYFIELD

您的结构定义如下所示:

// mystruct_def.h
BEGINSTRUCT(mystruct)
BITFIELD(a,int,1)
FIELD(b,int)
ARRAYFIELD(c,int,10)
ENDSTRUCT(mystruct)

你这样使用它:

// in mystruct.h file
#define DEFINE_STRUCT
#include "uglymacro.h"
#include "mystruct_def.h"

// in mystruct.c file
#include "mystruct.h"
#define DEFINE_READ
#include "mystruct_def.h"
#define DEFINE_WRITE
#include "mystruct_def.h"
坦率地说,根据现代标准,这种方法很难看。大约20年前我使用过类似的东西,当时它很难看。

另一种选择是使用更人性化的代码生成工具而不是C预处理器。

答案 2 :(得分:-1)

为什么不使用人类可读的文字格式?

typedef struct {
    int a : 1;
    /* ... just examples ... */
    int z : 13;
} data;

void save_data(FILE* fp, data d) {
    fprintf( fp, "a:%d\n", d.a );
    fprintf( fp, "b:%d\n", d.b );
    ...
    fprintf( fp, "z:%d\n", d.z );
}

这种技术的优势在于,使用任何不同语言的人都可以快速编写解析器,以便在任何机器,任何架构上加载数据。