C语言中的位场操作

C语言中的位场操作,c,bit-manipulation,C,Bit Manipulation,在C中测试和设置整数中的单个位的经典问题可能是最常见的中级编程技巧之一。您可以使用简单的位掩码进行设置和测试,例如 unsigned int mask = 1<<11; if (value & mask) {....} // Test for the bit value |= mask; // set the bit value &= ~mask; // clear the bit 但这让我畏缩 我的同事和我关于这件事的有趣争论至今仍未解决。这两种样式都

在C中测试和设置整数中的单个位的经典问题可能是最常见的中级编程技巧之一。您可以使用简单的位掩码进行设置和测试,例如

unsigned int mask = 1<<11;

if (value & mask) {....} // Test for the bit
value |= mask;    // set the bit
value &= ~mask;   // clear the bit
但这让我畏缩

我的同事和我关于这件事的有趣争论至今仍未解决。这两种样式都有效,我认为经典的位掩码方法简单、安全、清晰。我的同事也同意这是一种常见且简单的方法,但为了便于携带和安全起见,位域联合方法值得多出几行代码


双方还有什么争论吗?尤其是,位掩码方法可能会遗漏一些可能的故障(可能是endianness故障),但结构方法是安全的吗?

不管怎样,位字段在GNU软件中已经使用了几十年,并没有对它们造成任何伤害。我喜欢它们作为函数的参数

我认为,与结构相反,位字段是常规的。每个人都知道如何设置和设置各种选项的值,编译器将其归结为CPU上非常高效的按位操作

如果您以正确的方式使用掩码和测试,编译器提供的抽象应该使其健壮、简单、可读且干净


当我需要一组开/关开关时,我将继续在C中使用它们。

位字段并不像您想象的那样可移植,因为“C不保证机器字中字段的顺序”()


忽略这一点,只要使用正确,任何一种方法都是安全的。这两种方法还允许对积分变量进行符号访问。你可以争辩说,位域方法更容易编写,但它也意味着需要检查更多的代码。

你必须从作者的角度考虑这一点——了解你的受众。所以有两个“观众”需要考虑。p> 首先是典型的C程序员,他们一辈子都戴着比特面具,可以在睡梦中完成

其次是新手,他不知道这些东西是什么。他们在上一份工作中编写php,现在他们为您工作。(我这样说是作为一个做php的新手)

如果你写的东西是为了满足第一批读者(也就是整天的bitmask),你会让他们非常高兴,他们也可以蒙着眼睛维护代码。然而,新手在能够维护您的代码之前,可能需要克服一个巨大的学习曲线。他们需要学习二进制运算符,如何使用这些操作来设置/清除位,等等。几乎可以肯定的是,新手会引入bug,因为他/她掌握了使其工作所需的所有技巧

另一方面,如果您编写代码是为了满足第二个读者的需求,那么新手将更容易维护代码。他们会更容易摸索

 flags.force = 0;


第一批观众可能会变得暴躁,但很难想象他们不会摸索和维护新的语法。搞砸就难多了。不会有新的bug,因为新手将更容易维护代码。你会听到一些讲座,内容是“在我的时代,你需要一只稳定的手和一根磁化的针来设置位……我们甚至没有位面具!”(谢谢)

因此,我强烈建议使用位掩码上的字段来保护您的代码。

您所指的提及
raw
联合字段作为位字段的替代访问方法

博客作者使用的
raw
的目的是可以的,但是如果您计划将其用于其他任何事情(例如,位字段序列化、设置/检查单个位),灾难就在拐角处等着您。内存中位的顺序取决于体系结构,内存填充规则因编译器而异(请参阅),因此每个位字段的确切位置可能不同,换句话说,您永远无法确定每个位字段对应的
raw


然而,如果你不打算混合使用,你最好把
原始的
拿出来,这样你就安全了。

位域方法是什么让你畏缩的

这两种技术都有各自的位置,我唯一的决定是使用哪种技术:

对于简单的“一次性”位篡改,我直接使用位运算符

对于任何更复杂的东西,例如硬件寄存器映射,位场方法都会轻而易举地获胜

  • 位域的使用更加简洁 (以牺牲/略微/更多为代价) 写得冗长
  • 位字段是 更健壮(什么大小是“int”, (无论如何)
  • 位字段通常只是 与按位运算符一样快
  • 当您 混合使用单个和多个位 字段,并提取 多位字段涉及到 手动换档
  • 位字段是 有效地自我记录 定义结构,因此 命名元素,我知道它是什么 我本来打算这么做的
  • 位字段还无缝地处理大于单个int的结构
  • 对于按位运算符,典型的(不好的)做法是为位掩码定义大量的#定义

  • 关于位字段的唯一警告是确保编译器确实将对象压缩到了您想要的大小。我不记得这是否是由标准定义的,因此assert(sizeof(myStruct)==N)是一个有用的检查


一般来说,更容易阅读和理解的是更容易维护的。如果你的同事是C新手,那么“更安全”对他们来说,这种方法可能更容易理解。

结构映射不会出错,因为这两个字段都是可访问的,它们可以互换使用

位字段的一个好处是,您可以轻松聚合选项:

mask = USER|FORCE|ZERO|COMPAT;

vs

flags.user = true;
flags.force = true;
flags.zero = true;
flags.compat = true;
在某些环境中,例如处理协议选项时,必须单独设置选项或使用多个参数来设置协议选项可能会变得非常陈旧
 flags &= 0xFFFFFFFE;
mask = USER|FORCE|ZERO|COMPAT;

vs

flags.user = true;
flags.force = true;
flags.zero = true;
flags.compat = true;
// off the top of my head
#define SET_BIT(val, bitIndex) val |= (1 << bitIndex)
#define CLEAR_BIT(val, bitIndex) val &= ~(1 << bitIndex)
#define TOGGLE_BIT(val, bitIndex) val ^= (1 << bitIndex)
#define BIT_IS_SET(val, bitIndex) (val & (1 << bitIndex)) 
 #define  ASSERT_GPS_RESET()                    { P1OUT &= ~GPS_RESET ; }
typedef   unsigned long int     uint32_t
typedef unsigned int uint32_t

typedef union {
        struct {
                boolean_t user:1;
                boolean_t zero:1;
                boolean_t force:1;
                int :28;                /* unused */
                boolean_t compat:1;     /* bit 31 */
        };
        uint32_t raw;
} flags_t;
#define SET_BIT(val, bitIndex) val |= (1 << bitIndex)
  thread 1             thread 2
  LOAD field           LOAD field
  OR mask1             OR mask2
  STORE field          STORE field