通过位域访问char中的位

问题描述 投票:1回答:1

我想单独访问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。:我知道它可能会给混合字节序带来麻烦,但我只有小端。

c++ c char bit
1个回答
4
投票

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

TL; DR:不,不是。

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

未定义的是位顺序。编译器可以随意插入填充位或填充字节。所有这些都是实现定义的和不可移植的。所以你真的不知道_1在内存中的位置,或者它是MSB还是LSB。这些都没有明确定义。

但是,bits *b = (bits *)&c;通过结构指针访问char是严格的别名违规,也可能导致对齐问题。它在C和C ++中都是未定义的行为。你需要至少将这个结构显示为带有unionchar以避开严格的别名,但是你仍然可能得到对齐打嗝(并且C ++在通过联合打字时会皱眉)。

(从布尔类型到字符类型也可以给出一些真正的疯狂结果,请参阅_Bool type and strict aliasing


这一切都不方便 - 位域定义很差。简单地做:

c |= 1u << n;     // set bit n
c &= ~(1u << n);  // clear bit n

这是便携式,类型通用和与endianess无关。

(虽然由于隐式整数提升而避免更改签名,但最好始终将~的结果转换回预期类型:c &= (uint8_t) ~(1u << n);)。

请注意,类型char完全不适合按位运算,因为它可能会也可能不会被签名。相反,你应该使用unsigned char或最好使用uint8_t

© www.soinside.com 2019 - 2024. All rights reserved.