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
字段的某种校验和,我会在发送前计算它们。
我目前正在尝试找出一种优雅且方便的方法来在 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
字段的某种校验和,我会在发送前计算它们。