在嵌入式平台上。
假设我通过串行线从从设备接收字节,数据被正确序列化和反序列化,以确保字节顺序和数据大小正确。
我真正想要实现的是让struct test
具有可变大小,以允许将来扩展数组成员。
#include <stdio.h>
#include <stdint.h>
struct test
{
uint32_t a;
uint32_t b;
uint32_t c[];
};
union test1
{
struct test A;
uint8_t B[256];
};
int main(void)
{
union test1 test2;
for (uint32_t i=0; i<256; i++)
{
test2.B[i] = i;
}
for (size_t i=0; i<(sizeof(test2.B)/sizeof(uint32_t))-2; i++)
printf("Test: 0x%08X\n", test2.A.c[i]);
}
答案 0 :(得分:3)
需要考虑的一些事项:
对齐。您不能轻易地假设结构或联合不会有填充字节。从理论上讲,一些与int大小相关的模糊对齐要求的系统可能会导致结构中的填充字节。
由于该场景主要是理论上的,因此您可以通过添加
来确保不会发生这种情况_Static_assert(sizeof(struct test) == sizeof(int)+sizeof(int),
"Padding detected!");
正如问题所述,Endianess是一个真正的问题,必须在某处处理。
int
可能在联盟或嵌入式系统中的任何其他地方都没有任何意义。这些可能会在很多方面造成破坏,但不会发布任何代码。它们应该用stdint.h中的确定性大小和签名类型替换。stdint.h
。我不会那么担心。总的来说,我会说代码很好并且可以移植到所有有用的系统,只要你在某处处理endianess并摆脱int
。
答案 1 :(得分:2)
在C89和C99的原始出版物中,写入union
的一个成员并从另一个成员读取具有实现定义的行为。在TC1到C99中,它已更改为未指定的行为。无论哪种方式,实际意义都是相同的:你可以写一个联盟的一个成员并从另一个回读,而不用担心恶魔飞出你的鼻子;标准并没有告诉你你会得到什么值,但是如果知道实施它应该是可以预测的。
话虽如此,您很可能遇到struct test
中的填充,字节顺序不一致等问题。使用stdint.h
固定宽度类型而不是int
可以减轻其中一些问题,并尽可能使用无符号类型。我还强烈建议您从外部协议所处的任何明确的字节顺序中编写显式转换函数,例如
static int32_t
be32_to_cpu(const unsigned char *p)
{
uint32_t x = 0;
x |= ((uint32_t)p[0]) << 24;
x |= ((uint32_t)p[1]) << 16;
x |= ((uint32_t)p[2]) << 8;
x |= ((uint32_t)p[3]) << 0;
return (int32_t)x;
}
并使用手动计算的偏移量从unsigned char
缓冲区手动复制,例如
struct test
{
int32_t a;
int32_t b;
int32_t c[62];
}
void convert_block(struct test *restrict out,
const unsigned char *restrict buf)
{
out->a = be32_to_cpu(&buf[0]);
out->b = be32_to_cpu(&buf[4]);
for (int i = 0; i < 62; i++)
out->c[i] = be32_to_cpu(&buf[4 * (i+2)]);
}
现代编译器将识别be32_to_cpu
中的习语并生成最佳代码。对于小端,只需改变移位顺序。请注意,必须将值组合在无符号变量中并在之后转换为signed,因为转换为符号位具有未定义的行为。
如果您的有线协议发送可变大小的数据包,那么可能会有一个大小字段,您需要使用它们来知道何时停止读取,以及制作缓冲区的大小:
struct test
{
uint32_t size;
int32_t b;
int32_t c[]; /* SIZE/4 - 2 values */
};
struct test *
read_block(int fd)
{
char b1[4];
if (read(fd, b1, 4) < 4) abort();
uint32_t size = be32u_to_cpu(b1);
char b2[size - 4];
if (read(fd, b2, size - 4) < size - 4) abort();
struct test *out = malloc(size);
out->size = size;
out->b = be32s_to_cpu(&b2[0]);
for (int i = 0; i < size/4 - 2; i++)
out->c[i] = be32s_to_cpu(&b2[(i+1)*4]);
return out;
}
正确处理错误和短读作为练习。