在另一个变量的初始化器中使用联合字段

Use field of union in intialiser of another variable

我正在为软件模块的单元测试创​​建记录。记录数据在发送到 UUT 之前被序列化。

记录包含位域,所以我想在编译时使用这些相同的位域构建序列化记录(以避免必须考虑小端和大端问题以及位域中的位去向)并使用访问(序列化)数据的联合。我必须计算记录的校验和,所以我需要位域作为字节来执行此操作。

我目前的尝试是:

/* defines for 64 bit valid record */
#define REC3_ID        EEID_ARRAY_FIRST
#define REC3_SIZE      1
#define REC3_INDEX     248
#define REC3_SI0       MAKE_SIZE_INDEX0(REC3_SIZE,REC3_INDEX)
#define REC3_SI1       MAKE_SIZE_INDEX1(REC3_SIZE,REC3_INDEX)
#define REC3_VALUE0    0xf2
#define REC3_VALUE1    0x4f
#define REC3_VALUE2    0xb8
#define REC3_VALUE3    0xa0
#define REC3_DATA      \
    MAKE_CHKSUM7(REC3_ID,REC3_SI0,REC3_SI1,REC3_VALUE0,REC3_VALUE1,REC3_VALUE2,REC3_VALUE3),\
    REC3_ID,REC3_SI0,REC3_SI1,REC3_VALUE0,REC3_VALUE1,REC3_VALUE2,REC3_VALUE3

#define CHKSUM_SEED (0x2a)
#define MAKE_CHKSUM7(v0,v1,v2,v3,v4,v5,v6) (0x100-(((v0)+(v1)+(v2)+(v3)+(v4)+(v5)+(v6)+CHKSUM_SEED)%0x100))

typedef union
{
    uint8_t si[2];
    struct
    {
        uint16_t s: 6;
        uint16_t i: 10;
    } b;
} si_t;

MAKE_SIZE_INDEX0(size,index) ((si_t){.b.s=size,.b.i=index}).si[0]
MAKE_SIZE_INDEX1(size,index) ((si_t){.b.s=size,.b.i=index}).si[1]

static uint8_t rec3[] = {REC3_DATA};

问题出在宏 MAKE_SIZE_INDEX0MAKE_SIZE_INDEX1 上。我无法让它们编译 (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.11))

问题可以简化为:

uint8_t rec[] = {0x12, (si_t){.b.s=4,.b.i=8}.si[0], (si_t){.b.s=4,.b.i=8}.si[1], 0x34};

但这会导致错误:

error: initializer element is not constant

我知道我可以在 运行 时创建我的记录,但我想知道是否可以让预处理器处理它。

我的替代方案是这样的:

#if defined (TGT_ARCHITECTURE_x86_64)
#define MAKE_SIZE_INDEX0(size,index) (((size)&0x3f)+(((index)<<6)&0xc0))
#define MAKE_SIZE_INDEX1(size,index) ((index>>2)&0xff)
#endif

但这取决于目标是小端还是大端以及它如何存储位域。

static uint8_t rec3[] = { (si_t){.b.s=4,.b.i=8}.si[0] };

具有静态存储持续时间的变量只能使用静态初始化程序进行初始化 - 它必须是常量表达式。常量表达式中不允许有 list of what is allowed in a constant expression - using array subscript operator on a array embedded in a compound literal。基本和你做不到的一样static int a[] = {1, 2}; static int b = a[1];

附带说明一下,该标准表示允许实现接受常量表达式的自定义形式。因此代码可能恰好适用于不同的编译器甚至不同的 gcc 版本(与最新的 gcc 版本一样,您可以使用 const 限定变量初始化具有静态存储持续时间的变量,这是一个扩展)。

编译器错误 "initializer element is not constant",因为用于初始化具有静态存储持续时间的变量的元素不是常量表达式。

使用位字段提取变量的位掩码依赖于编译器,依赖于编译器选项 (gcc storage layout),不应在可移植代码中使用。编译器可以自由地重新排序结构中的位域,并且可以自由地在位域成员之间添加填充。正如在 Whosebug 上多次宣传的那样,使用位掩码 - 它们每次都有效。