C/C++位域概念及位域并发修改问题

    在计算机中,数据是以0和1两种形态进行表述的,而每个0和1都占据了一个位(bit)的大小,8个位(bit)可以组成一个字节(byte),一个字节就是计算机里数据类型的最小基本单位,如:char在32bit系统中大小为一个字节(byte)。但我们应该知道,虽然字节是最小的数据类型基本单位,但有时候我们或许是用不完这一个字节的,如:我们要表示一个开关量的时候,它就两种情况:0和1嘛,若我们申请了一个字节,那剩下的七个位岂不是浪费了?所以C中就有了位域(bit-field)的概念,后来C++为了支持底层编程也从C中继承来了这个概念(它属于固有的不可移植特性,不可移植特性指的是依赖于具体机器的特性)。

    位域的定义形式:变量名 : 具体大小,如: 

struct bf { 
    char a : 4; 
    char b : 4;
} 

上面例子的意思很简单:在结构体内定义了一个占四位的char类型变量a,以及另一个占四位的char类型变量b.我们可以用代码sizeof(bf)测试一下bf的大小,我们可以发现bf大小为1,而不是2,OK,看来这样确实可以节省空间。不过有时候位域的大小可不是简单地把所有位域声明的大小加起来哦,这还涉及到一个内存对齐的问题,这个问题留到以后说,目前只是先了解一下。还有一个问题需要注意一下,或许我们会注意到,a和b各占了四位,那加起来就刚好是一个char类型的大小咯?那是不是它们平分的是同一个字节呢?对于这个问题,以下代码可以检验:

#include <stdio.h>
#include <string.h> 

typedef struct {
    char a : 4;
    char b : 4;
} bf;

int main(int argc, const char *argv[]) {
    bf f;
    f.a= 0x0a; //0x06
    f.b=0x05; //0x03
    unsigned char x[sizeof(bf)];
    memcpy(x, &f, sizeof(f));
    
    for(int i=0;i<sizeof(bf)*8/4;i++)
    {
        if(i%2==0)printf("%1x ",x[i / 2]&0x0F); //低半字节
            else printf("%1x ", (x[i / 2] & 0xF0)>>4 ); //高半字节
                }
    //如果在相同字节,就是连续的,否则是不连续的。
    //也可以直接检测。
    //如果觉得不保险,可以用两组数据检测。
    //如果非要编译期检测,那当然是不可能的。
    return 0;
    
}

    好了,说了位域的概念,那就可以进入重点部分了。线程安全问题是一个蛮重要的问题,毕竟很多程序是离不开多线程的,而接下来要说的就是关于位域的并发修改问题,我们以一个例子来说明:   

struct { 
    char a; 
    int b : 5, 
    c : 11, 
    : 0, 
    d : 8; 
    struct { int ee : 8; } e;
}

我们就先来说说什么样的情况修改是安全的吧,再基于这些情况进行一些简单的分析,安全情况如下:

    1.被存放在不同内存位置的一个位域对象和一个非位域对象,对它们进行并发修改是安全的; 

    2.若两者皆为位域对象,而其中一个被声明于内嵌结构体中,另一个不是,则它们可以安全地并发修改;

    3.若两者被一个零长位域对象分割,则它们也是可以安全地并发修改;

    4.若两者被一个非位域对象声明分割,则它们可以并发地安全修改.对于上面的例子,a和d可以安全并发修改,基于第一条;d和e.ee也是可以安全并发修改的,基于第二条;a,d和e.ee三者都可以安全并发修改,基于一和二;b,c不能安全地并发修改,因为它们不符合上述任意一条,而a和b则是可以的,基于一。

    因此,我们只需根据上述几条去判断,则可以判断是否可以安全并发修改,这也为我们编写线程安全的代码提供了一些指示。

 


版权声明:本文为oYinGui1原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。