c中的自定义数据类型:4x14bit + 64位中的1x8bit 'container'

custom data type in c: 4x14bit + 1x8bit within a 64 bit 'container'

我目前正在尝试找出一种优雅且方便的方法来在 64 位边界内存储 4 个 14 位值和 1 个 8 位值。

像这样:

typedef struct my64bit{
    unsigned data1 : 14;
    unsigned data2 : 14;
    unsigned data3 : 14;
    unsigned data4 : 14;
    unsigned other : 8;
}tmy64Bit;

稍后我不想创建这些数组 'containers'

tmy64Bit myArray[1000];

所以我有一个指针 "myArray" 指向 1000x64 位内存

这个数组通过 tcp 发送到嵌入式-linux SOCFPGA 系统,在那里它应该被复制(修正字节顺序和网络字节顺序)到一个特定的内存(直接从 fpga 访问)

我的问题是上面的代码没有创建 64 位类型

sizeof(tmy64Bit)

returns 12,所以分配了 12 个字节而不是 8

用数据填充结构并观察内存(在我的 64 位 linux 系统上)returns 这个

tmy64Bit test;
memset(&test,0,sizeof(tmy64Bit));
test.data1 = 0x3fff;
...
test.other = 0xAA;

Memory View:
after d1 written = 0xFF3F0000 00000000 00000000
after d2 written = 0xFFFFFF0F 00000000 00000000
after d3 written = 0xFFFFFF0F FF3F0000 00000000
after d4 written = 0xFFFFFF0F FFFFFF0F 00000000
after o  written = 0xFFFFFF0F FFFFFF0F AA000000

因此前 2 个 14 位变量存储正确,但随后填充填充最后半字节,最后最后一个字节需要存储在新的 64 位单元格中

另一种方法是

typedef struct my2nd64Bit{
 uint8_t data[7];
 uint8_t other;
}tmy2nd64Bit;

其中一个

sizeof(tmy2nd64Bit)

returns 8(预期)

这会生成正确的填充结构,但存储 14 位总是涉及大量的位移和掩码

这就是你想要的:

typedef struct my64bit{
    uint64_t data1 : 14;
    uint64_t data2 : 14;
    uint64_t data3 : 14;
    uint64_t data4 : 14;
    uint64_t other : 8;
}tmy64Bit;

unsigned表示unsigned int,这种类型在大多数系统上都是32位的。这将导致填充,因为不允许各个字段跨越 32 位边界。使用 64 位成员类型不会为这种情况添加填充(您不会跨越任何 64 位边界)。

至于任何关于位域的问题,您需要记住,大多数位域机制都是实现定义的,这意味着如果您想要使用它,您应该检查您是否真的得到了您想要的.此外,如果您打算使用其他编译器,请检查行为是否相同(通常是,但在异国情调的平台上可能不同)。如果你检查得当,这是可以安全使用的(不是未定义的行为),但你可能想使用更便携的方式,例如使用位操作。

避免使用位域,它们在 C 标准中的定义很差,几乎无法在实践中使用。您的位域结构代码包含大约 5 到 10 种不同形式的不明确行为。 C 标准位域是一个危险且多余的特性,就这么简单。

相反,只需使用二进制值的原始数组,如下所示:

typedef union {
  uint8_t   array [sizeof(uint64_t)];
  uint64_t  val64;
}tmy64Bit;

(请注意联合中的 uint64_t 将取决于字节顺序)

在这种原始数组中设置和清除位的实际方法是:

void set_bit (tmy64Bit* x, size_t bit)
{
  x->array [bit / 8] |= 1 << (bit % 8);
}

void clear_bit (tmy64Bit* x, size_t bit)
{
  x->array [bit / 8] &= ~(1 << (bit % 8));
}

或者,如果你愿意,一个更具可读性的版本(等效):

void set_bit (tmy64Bit* x, size_t bit)
{
  uint8_t byte_index = bit / 8;
  uint8_t bit_index  = bit % 8;

  x->array[byte_index] |= 1 << bit_index;
}

我同意 ,但对于这种特殊情况,我的实现会有点不同(没有双关语)。

首先,我将决定如何将字段打包到每个 64 位字中。例如:

 Bits    Description
 0-13    data[0]
14-27    data[1]
28-41    data[2]
42-55    data[3]
56-64    other

其次,我将在 C99 灵活数组成员中使用带有 FPGA 数据的动态分配结构来描述目标设备:

typedef struct {
    /* Other FPGA-related fields, maybe
     * a struct sockaddr_in or _in6
     * to identify the FPGA */
    size_t   words;
    uint64_t word[];
} fpga;

fpga *fpga_create(const size_t words)
{
    fpga *f;

    f = malloc(sizeof (fpga) + word * sizeof(f->word[0]));
    if (!f)
        return NULL;

    f->words = words;
    memset(f->word, 0, f->words * sizeof (f->word[0]));        

    return f;
}

第三,我会使用 static inline 访问器来操作数据:

static inline unsigned int fpga_get_data(const fpga *f, const int w, const int i)
{
    assert(f != NULL);
    assert(w >= 0 && (size_t)w < f->words);
    assert(i >= 0 && i < 4);

    return (f->word[(size_t)w] >> (i * 14)) & 0x3FFFU;
}

static inline unsigned int fpga_get_other(const fpga *f, const int w)
{
    assert(f != NULL);
    assert(w >= 0 && (size_t)w < f->words);

    return (f->word[(size_t)w] >> 56) & 0xFFU;
}

static inline void fpga_set_data(const fpga *f, const int w, const int i,
                                 const unsigned int value)
{
    assert(f != NULL);
    assert(w >= 0 && (size_t)w < f->words);
    assert(i >= 0 && i < 4);

    f->word[(size_t)w] = (f->word[(size_t)w] & (~(0x3FFFU << (i*14))))
                       | ((value & 0x3FFFU) << (i*14));
}

static inline void fpga_set_other(const fpga *f, const int w, const unsigned int value)
{
    assert(f != NULL);
    assert(w >= 0 && (size_t)w < f->words);

    f->word[(size_t)w] = (f->word[(size_t)w] & (uint64_t)0x00FFFFFFFFFFFFFFULL)
                       | ((value & 0xFFU) << 56);
}

上面,w是单词的索引,i是数据条目的索引(0到3)。如果你想要一个连续的 data 数组,你可以使用

static inline unsigned int fpga_get_data(const fpga *f, const int i)
{
    assert(f != NULL);
    assert(i >= 0 && (size_t)i < 4 * f->words);

    return (f->word[(size_t)i / 4] >> ((i & 3) * 14)) & 0x3FFFU;
}



    if (f != NULL &&
        i >= 0 && i < (size_t)4 * f->words)
        return (f->word[(size_t)i / 4] >> ((i & 3) * 14)) & 0x3FFFU;
    else
        return 0U; /* Or abort with an error */
}

static inline void fpga_set_data(const fpga *f, const int i, const unsigned int value)
{
    assert(f != NULL);
    assert(i >= 0 && (size_t)i / 4 < f->words);

    f->word[(size_t)i / 4] = (f->word[(size_t)i / 4] & (~(0x3FFFU << ((i & 3) * 14))))
                           | ((value & 0x3FFFU) << ((i & 3) * 14));
}

访问器应该在定义结构的头文件中定义。

请注意,如果 other 实际上是四个 data 字段的某种校验和,我会在发送前计算它们。