如何在数组中打包结构并删除零填充?
How to pack structs in array and remove zero-padding?
据我了解,"there will never be padding in between elements of an array"。而且我知道一个结构必须至少有一个字节长,否则它将用零填充。
我想要一个结构数组,每个结构的大小为 4 位,没有零填充。我可以对阵列应用某种“包装”吗?
我希望输出为 0xFFFF (0b1111_1111_1111_1111),但我无法摆脱结构的填充。
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main() {
struct data_struct {
unsigned a: 1;
unsigned b: 3;
} __attribute__((packed)); // avoid structure padding
union {
struct data_struct data[4];
uint16_t data_uint;
} union_data;
memset(&union_data.data_uint, 0, sizeof(union_data.data_uint));
for (int i = 0; i < 4; ++i) {
union_data.data[i].a = 1;
union_data.data[i].b = 7;
}
printf("union_data = 0x%04X\n", union_data.data_uint); // 0x0F0F == 0b0000_1111_0000_1111
return 0;
}
Is there some kind of "packing" I can apply to the array?
不,没有。字节是最低的可寻址单位,它至少有 8 位 - 因此所有变量将至少对齐 8 位,并且大小至少为 8 位。
How to pack structs in array and remove zero-padding?
不要。编写访问器函数并使用位操作来分配和检索数据。更喜欢编写可移植代码。
最好不要使用位域 - 请注意,字节内位域的顺序(LSB 与 MSB)是实现定义的,位域之间的填充也是实现定义的。为了便于移植,请编写带有位操作的访问器函数。
想法是 struct data_struct data[4]
中的第二个和第四个元素将从字节边界的中间开始——这是不可能的。对于你的情况,如果你想以这种方式访问它们,你必须从正确对齐结构内的打包联合中提取数据:
union union_data_t {
struct {
unsigned char a1 : 1;
unsigned char b1 : 3;
unsigned char a2 : 1;
unsigned char b2 : 3;
} data[2];
uint16_t data_uint;
} union_data;
struct mydata union_data_get(union union_data_t *t, unsigned idx) {
struct mydata r;
r.a = idx%2 ? t->data[idx/2].a2 : t->data[idx/2].a1;
r.b = idx%2 ? t->data[idx/2].b2 : t->data[idx/2].b1;
return r;
}
void union_data_get(union union_data_t *t, unsigned idx, struct mydata mydata) {
if (idx%2) { t->data[idx/2].a2 = mydata.a; }
else { t->data[idx/2].a1 = mydata.a; }
if (idx%2) { t->data[idx/2].b2 = mydata.b; }
else { t->data[idx/2].b1 = mydata.b; }
}
听起来像是最好的特定于 gcc 的抽象,但现在无论如何都没有理由使用位域 - 无论如何都可以使用位操作来编写访问器函数:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
struct data_struct {
unsigned a: 1;
unsigned b: 3;
} __attribute__((packed)); // avoid structure padding
struct data_struct data_struct_array_get(unsigned char *t, unsigned idx) {
const unsigned mask = 4 * (idx % 2);
unsigned v = (t[idx/2] >> mask) & 0xf;
return (struct data_struct){v>>3, v};
}
void data_struct_array_set(unsigned char *t, unsigned idx, struct data_struct data) {
const unsigned v = data.a << 3 | data.b;
const unsigned mask = 4 * (idx % 2);
t[idx/2] &= ~(0xf << mask);
t[idx/2] |= v << mask;
}
int main() {
union union_data_t {
unsigned char data[2];
uint16_t data_uint;
} union_data;
for (int i = 0; i < 4; ++i) {
data_struct_array_set(union_data.data, i,
(struct data_struct){1, 7}
);
}
printf("union_data = 0x%04X\n", union_data.data_uint);
return 0;
}
据我了解,"there will never be padding in between elements of an array"。而且我知道一个结构必须至少有一个字节长,否则它将用零填充。
我想要一个结构数组,每个结构的大小为 4 位,没有零填充。我可以对阵列应用某种“包装”吗?
我希望输出为 0xFFFF (0b1111_1111_1111_1111),但我无法摆脱结构的填充。
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main() {
struct data_struct {
unsigned a: 1;
unsigned b: 3;
} __attribute__((packed)); // avoid structure padding
union {
struct data_struct data[4];
uint16_t data_uint;
} union_data;
memset(&union_data.data_uint, 0, sizeof(union_data.data_uint));
for (int i = 0; i < 4; ++i) {
union_data.data[i].a = 1;
union_data.data[i].b = 7;
}
printf("union_data = 0x%04X\n", union_data.data_uint); // 0x0F0F == 0b0000_1111_0000_1111
return 0;
}
Is there some kind of "packing" I can apply to the array?
不,没有。字节是最低的可寻址单位,它至少有 8 位 - 因此所有变量将至少对齐 8 位,并且大小至少为 8 位。
How to pack structs in array and remove zero-padding?
不要。编写访问器函数并使用位操作来分配和检索数据。更喜欢编写可移植代码。
最好不要使用位域 - 请注意,字节内位域的顺序(LSB 与 MSB)是实现定义的,位域之间的填充也是实现定义的。为了便于移植,请编写带有位操作的访问器函数。
想法是 struct data_struct data[4]
中的第二个和第四个元素将从字节边界的中间开始——这是不可能的。对于你的情况,如果你想以这种方式访问它们,你必须从正确对齐结构内的打包联合中提取数据:
union union_data_t {
struct {
unsigned char a1 : 1;
unsigned char b1 : 3;
unsigned char a2 : 1;
unsigned char b2 : 3;
} data[2];
uint16_t data_uint;
} union_data;
struct mydata union_data_get(union union_data_t *t, unsigned idx) {
struct mydata r;
r.a = idx%2 ? t->data[idx/2].a2 : t->data[idx/2].a1;
r.b = idx%2 ? t->data[idx/2].b2 : t->data[idx/2].b1;
return r;
}
void union_data_get(union union_data_t *t, unsigned idx, struct mydata mydata) {
if (idx%2) { t->data[idx/2].a2 = mydata.a; }
else { t->data[idx/2].a1 = mydata.a; }
if (idx%2) { t->data[idx/2].b2 = mydata.b; }
else { t->data[idx/2].b1 = mydata.b; }
}
听起来像是最好的特定于 gcc 的抽象,但现在无论如何都没有理由使用位域 - 无论如何都可以使用位操作来编写访问器函数:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
struct data_struct {
unsigned a: 1;
unsigned b: 3;
} __attribute__((packed)); // avoid structure padding
struct data_struct data_struct_array_get(unsigned char *t, unsigned idx) {
const unsigned mask = 4 * (idx % 2);
unsigned v = (t[idx/2] >> mask) & 0xf;
return (struct data_struct){v>>3, v};
}
void data_struct_array_set(unsigned char *t, unsigned idx, struct data_struct data) {
const unsigned v = data.a << 3 | data.b;
const unsigned mask = 4 * (idx % 2);
t[idx/2] &= ~(0xf << mask);
t[idx/2] |= v << mask;
}
int main() {
union union_data_t {
unsigned char data[2];
uint16_t data_uint;
} union_data;
for (int i = 0; i < 4; ++i) {
data_struct_array_set(union_data.data, i,
(struct data_struct){1, 7}
);
}
printf("union_data = 0x%04X\n", union_data.data_uint);
return 0;
}