通过位域访问char中的位

时间:2019-04-05 08:50:49

标签: c++ c char bit

我想分别访问char中的位。关于此主题,这里有一些问题和答案,但都建议使用布尔数学。但是,对于我来说,如果我可以简单地单独命名这些位,将更加方便。所以我当时想像这样通过位域访问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 .:我知道这可能给混合字节序带来麻烦,但是我的字节序很少。

1 个答案:

答案 0 :(得分:4)

  

这是C语言中定义的行为吗?
  这是C ++中定义的行为吗?

TL; DR:不,不是。

布尔型位域的定义很明确:bool是用于位域的确定类型,因此可以确保在内存中的某个位置分配8个布尔值的blob。如果您访问布尔值_1,则将获得与上次访问该变量时相同的值。

未定义的是位顺序。编译器可以根据需要插入填充位或填充字节。所有这些都是实现定义的并且不可移植。因此,您真的无法知道_1在内存中的位置,或者是MSB还是LSB。这些都没有明确定义。

但是,bits *b = (bits *)&c;通过结构指针访问char是严格的别名冲突,并且还可能导致对齐问题。在C和C ++中,这都是未定义的行为。您至少需要将此结构显示为带有union的{​​{1}}才能规避严格的别名,但是您仍然可能会遇到对齐问题(并且C ++会通过联合将其皱起眉头)。

(从布尔类型转换为字符类型也会产生一些真正的疯狂结果,请参见_Bool type and strict aliasing


所有这些都不方便-位域定义很差。简单地做一下 更好:

char

这是可移植的,键入泛型且与Endianess无关。

(尽管可以避免由于隐式整数提升而引起的符号变化,但最好将c |= 1u << n; // set bit n c &= ~(1u << n); // clear bit n 的结果强制转换回预期的类型:~)。

请注意,类型c &= (uint8_t) ~(1u << n);完全不适合按位算术,因为它可能带有符号也可能没有符号。相反,您应该使用char或最好使用unsigned char