假设我有这个结构
struct 16bits{
int8_t a;
int8_t b;
}
和一个 16 位序列
0001 0011 0111 1111
如果我将它转换为 16 位结构,它会是这个样子吗?
a = 0001 0011
b = 0111 1111
我假设你的意思是这样的:
// this points at your 16-bit sequence
unsigned char *input_data = ...
struct 16bits *output_data = ( struct 16bits * ) input_data;
uint8_t a_bits = output_data->a;
uint8_t b_bits = output_data->b;
一般来说,不,你不能假设你能做到这一点。一般来说,这将是a strict aliasing violation 和未定义的行为。 “严格别名”规则基本上说您不能将内存视为它不是的东西 - 但除了您始终可以一次访问任何一个 char 之外。 int 不是 float。
此外,正如您提到的,结构中的字段之间可以有填充。
不过,在您的具体示例中,几乎可以肯定,它几乎可以在任何平台上“工作”,因为int8_t 几乎可以肯定是signed char,struct 16bits 中几乎肯定没有填充,并且任何内存始终可以作为char 值访问。
不过,将char 类型替换为double 或int64_t 等类型,您可能会遇到对齐和填充问题。在某些平台上,这种严格的别名违规可能会导致代码以SIGSEGV 或SIGBUS 失败。
假设 8 位 char 值,所以 int8_t 实际上是 char,作为两个 8 位值访问应用于 16 位序列的任何数据类型的完全符合标准的方法将是
// assume this points to your 16-bit sequence
unsigned char *input_data = ...
// create a structure that we can actually copy the bits into
struct 16bits output_data;
memcpy( &output_data, input_data, sizeof( output_data ) );
请注意,如果结构包含char 以外的类型的元素,则可能存在填充。如果你使用#pragma pack 之类的东西来消除填充,you can wind up with code that doesn't run on some platforms.
诸如您提供的链接中的代码很猖獗-实际上是未定义的行为。但它“有效”是因为编写最流行的已发布代码的 x86 平台非常、非常、非常容忍未对齐的访问(尽管仍然存在性能损失)。但是这种类型的代码在任何有对齐要求的平台上都不能很好地工作。只需Google pragma pack sigbus,您就会发现很多程序员的例子,例如,当在 x86 上运行良好的代码在 ARM 或 SPARC 平台上失败时,程序员会感到惊讶。