在C语言中进行位结构编程

6
这是如何工作的?
struct {
    int a : 21;
    int b : 11;
};

a和b是两个独立的int变量还是使用不同的位域表示的相同变量?
3个回答

8

这是一个 struct 中的两个独立变量,一个名为 a,另一个名为 b。然而,它们的大小分别为 21 位和 11 位,访问并操纵一个变量不会影响另一个变量。

希望这可以帮到您!


谢谢。所以本质上两者都是32位整数,但只使用了a的21位和b的11位,保留其他位未被利用?或者编译器会以某种方式进行优化吗?有人可能更喜欢使用一个32位变量并将其分成21:11位字段... - AbhinavChoudhury
@AbhinavChoudhury:是的,其他位将不会被使用。即使没有位域,也会发生同样的事情,考虑声明struct s { int x; char y; };在大多数系统上使用8个字节。 - Dietrich Epp
@AbhinavChoudhury 之所以它们是2个“32位”(或16位,取决于平台)整数,仅仅是因为系统处理内存的方式。如果你声明int a:5,CPU也会将这个变量存储在32位(可能是16位,取决于系统)中,因为它是一个整数,整数在内存中占据一定的大小(同样取决于平台,在我的系统上,int的大小为2字节,但在你的系统上不一定是这样)。在你的情况下,“:21”和“:11”只是限制了你可以分配给这个变量的最大值。 - H_squared
1
顺便提一句,如果你想将它们存储在同一个32位寄存器中,你可以这样做(考虑到int类型长度为4个字节):int ab=a<<11+b - H_squared
@DietrichEpp:您根据什么断言会使用两个32位实体,多余的位将不被使用,而不是将ab打包到一个32位实体中?如果连续的字段完全适合,则C标准要求将它们打包到实现正在使用的任何单元中。 (如果它们不适合,则标准允许在一个单元中部分地放置字段并在下一个单元中留下未使用的位或离开位。) - Eric Postpischil

2
“variables”在这里的意思并不是完全清楚。如果你指的是可以取地址的内存块,那么位域不符合描述,因为它们是“可寻址存储单元”的一部分。如果你指的是“我可以存储某个值的一组位”,那么a和b看起来与结构体中的任何其他字段相同。
尽管如此,对于足够的语义争辩,我们还是要去看源代码:
C99标准的6.7.2.1章节说:
一个实现可以分配任何可寻址存储单元,足够大以容纳位域。如果有足够的空间,紧随结构体中另一个位域之后的位域应该被打包到同一单元的相邻位中。如果剩余空间不足,则不适合的位域是否放入下一个单元或重叠相邻单元是由实现定义的。在单元内分配位域的顺序(从高位到低位或从低位到高位)由实现定义。可寻址存储单元的对齐方式未指定。
因此,根据编译器的具体规范,取决于编译器选择的“可寻址存储单元”,您的 a 和 b 可能会结束在不同的“存储单元”中。

说实话,在C标准中,那些东西应该被称为“条款”,而不是“章节”(关于你缩写“ch”的问题)。 - Eric Postpischil

0

这取决于系统架构如何实现。如果您采用更直接的例子:

struct x {
  uint8_t a : 2;
  uint8_t b : 3;
};

这里有可能会出现ab共享同一字节的内存,例如:xxxb bbaa(其中x未使用)。

我认为对齐和打包的语义是未定义的 - 最好尝试一下!如果您正在进行多平台项目,请注意不同的硬件(甚至编译器)。

struct x s;
s.a = 3;
s.b = 5;
printf("0x%02X\n", *((char*)&s) );

输出可能是任何一个0x030x050x16或者甚至是0x25

我希望看到的是0x16...

在我看来,当传输位域时,你应该始终手动构建已知状态。

uint8_t buf[];
buf[x] = 0;
buf[x] |= (s.a) & 0x03;
buf[x] |= (s.b << 2) & 0x1C;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接