通过位域访问 char 中的位
Accessing the bits in char through a bitfield
我想单独访问 char
中的位。 SO 上有几个关于此主题的问题和答案,但它们都建议使用布尔数学。但是,对于我的使用来说,如果我可以简单地分别命名这些位会更方便。所以我想通过位域访问 char
,就像这样
#include <stdbool.h>
#include <stdio.h>
typedef struct {
bool _1 : 1, _2 : 1, _3 : 1, _4 : 1, _5 : 1, _6 : 1, _7 : 1, _8 : 1;
} bits;
int main() {
char c = 0;
bits *b = (bits *)&c;
b->_3 = 1;
printf("%s\n", c & 0x4 ? "true" : "false");
}
此编译没有错误或警告 gcc -Wall -Wextra -Wpedantic test.c
。当 运行 使用 valgrind
生成的可执行文件时,它不会报告任何内存错误。为 b->_3 = 1;
分配生成的程序集是 or eax, 4
,这是合理的。
问题
- 这是在 C 中定义的行为吗?
- 这是在 C++ 中定义的行为吗?
N.B.: 我知道这可能会给混合字节顺序带来麻烦,但我只有小字节顺序。
Is this defined behaviour in C?
Is this defined behaviour in C++?
TL;DR:不,不是。
布尔位域定义明确:bool
是用于位域的 ok 类型,因此您可以保证在内存中的某处分配 8 个布尔值。如果您访问布尔值 _1
,您将获得与上次访问该变量时相同的值。
没有定义的是位序。编译器可以根据需要插入填充位或填充字节。所有这些都是实现定义的且不可移植的。所以你无法真正知道 _1
在内存中的位置,或者它是 MSB 还是 LSB。 None 其中定义明确。
但是,bits *b = (bits *)&c;
通过结构指针访问 char
是严格的别名冲突,并且还可能导致对齐问题。它在 C 和 C++ 中都是未定义的行为。您至少需要将此结构显示为带有 char
的 union
以避开严格的别名,但您仍然可能会遇到对齐问题(并且 C++ 不喜欢通过联合进行类型双关)。
(从布尔类型到字符类型也会产生一些真正疯狂的结果,参见 )
None 这很方便 - 位域定义很差。 更好地简单地做:
c |= 1u << n; // set bit n
c &= ~(1u << n); // clear bit n
这是可移植的,类型通用且与字节顺序无关。
(虽然为了避免由于隐式整数提升而导致的符号变化,但最好始终将 ~
的结果转换回预期类型:c &= (uint8_t) ~(1u << n);
)。
请注意,类型 char
完全不适合按位运算,因为它可能有符号也可能没有符号。相反,您应该使用 unsigned char
或最好使用 uint8_t
.
我想单独访问 char
中的位。 SO 上有几个关于此主题的问题和答案,但它们都建议使用布尔数学。但是,对于我的使用来说,如果我可以简单地分别命名这些位会更方便。所以我想通过位域访问 char
,就像这样
#include <stdbool.h>
#include <stdio.h>
typedef struct {
bool _1 : 1, _2 : 1, _3 : 1, _4 : 1, _5 : 1, _6 : 1, _7 : 1, _8 : 1;
} bits;
int main() {
char c = 0;
bits *b = (bits *)&c;
b->_3 = 1;
printf("%s\n", c & 0x4 ? "true" : "false");
}
此编译没有错误或警告 gcc -Wall -Wextra -Wpedantic test.c
。当 运行 使用 valgrind
生成的可执行文件时,它不会报告任何内存错误。为 b->_3 = 1;
分配生成的程序集是 or eax, 4
,这是合理的。
问题
- 这是在 C 中定义的行为吗?
- 这是在 C++ 中定义的行为吗?
N.B.: 我知道这可能会给混合字节顺序带来麻烦,但我只有小字节顺序。
Is this defined behaviour in C?
Is this defined behaviour in C++?
TL;DR:不,不是。
布尔位域定义明确:bool
是用于位域的 ok 类型,因此您可以保证在内存中的某处分配 8 个布尔值。如果您访问布尔值 _1
,您将获得与上次访问该变量时相同的值。
没有定义的是位序。编译器可以根据需要插入填充位或填充字节。所有这些都是实现定义的且不可移植的。所以你无法真正知道 _1
在内存中的位置,或者它是 MSB 还是 LSB。 None 其中定义明确。
但是,bits *b = (bits *)&c;
通过结构指针访问 char
是严格的别名冲突,并且还可能导致对齐问题。它在 C 和 C++ 中都是未定义的行为。您至少需要将此结构显示为带有 char
的 union
以避开严格的别名,但您仍然可能会遇到对齐问题(并且 C++ 不喜欢通过联合进行类型双关)。
(从布尔类型到字符类型也会产生一些真正疯狂的结果,参见
None 这很方便 - 位域定义很差。 更好地简单地做:
c |= 1u << n; // set bit n
c &= ~(1u << n); // clear bit n
这是可移植的,类型通用且与字节顺序无关。
(虽然为了避免由于隐式整数提升而导致的符号变化,但最好始终将 ~
的结果转换回预期类型:c &= (uint8_t) ~(1u << n);
)。
请注意,类型 char
完全不适合按位运算,因为它可能有符号也可能没有符号。相反,您应该使用 unsigned char
或最好使用 uint8_t
.