C语言 day18 | 我的日常分享

C语言 day18

结构体

位段

  1. 信息在计算的存取长度一般以字节为单位;
  2. 有事储存一个信息不必用一个字节或多个字节
    • 例如:真或假:用0或1,只需一位即可.

怎样向一个字节的一个或几个二进制位赋值和改变他的值呢

  1. 利用前面学过的为运算符:<< >> & | ~ ^
  2. 结构体定义位段,利用位段可以减少存储空间并简化位的操作

C语言允许一个结构体中以位为单位来指定其成员所占内存长度,以位为单位的成员为“位段”或称“位域”。

注意:位段一般用的是unsigned intunsigned char

案例1:

  • 位域的存储
  1. 当相邻成员的类型相同时,如果它们的位宽之和小于类型的 sizeof 大小,那么后面的成员紧邻前一个成员存储,直到不能容纳为止;如果它们的位宽之和大于类型的 sizeof 大小,那么后面的成员将从新的存储单元开始,其偏移量为类型大小的整数倍。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<stdio.h>
struct data1{
unsigned char a:2;
unsigned char b:2;
};
struct data2{
unsigned char a:2;
unsigned char b:7;
};
struct data3{
unsigned int a:12;
unsigned int b:12;
};
struct data4{
unsigned int a:12;
unsigned int b:21;
};
int main(int argc,char *argv[]){
printf("%d\n",sizeof(struct data1));
printf("%d\n",sizeof(struct data2));
printf("%d\n",sizeof(struct data3));
printf("%d\n",sizeof(struct data4));
return 0;
}
````
运行结果:
![图片](./pic1.png)

分析: data1中成员类型相同,共4位小于char的类型位数8位即1字节,所以打印1字节,data2,共9位,超了,会另起一行,所以打印2字节;data3成员类型相同,且共24位没有超过32位即4字节,所以打印4字节,而data4成员共33位超了,故打印8字节

2. 当相邻成员的类型不同时,不同的编译器有不同的实现方案,GCC 会压缩存储,而 VC/VS 不会。

```C
#include<stdio.h>
struct data1{
unsigned char a:2;
unsigned int b:2;
};
struct data2{
unsigned char a;
unsigned int b;
};
int main(int argc,char *argv[]){
printf("%d\n",sizeof(struct data1));
printf("%d\n",sizeof(struct data2));
return 0;
}

分析:
在 GCC 下的运行结果为 1,三个成员挨着存储;在 VC/VS 下的运行结果为 8(为什么不是5?见上篇文章结构体内存对齐),三个成员按照各自的类型存储(与不指定位宽时的存储方式相同)。

  1. 如果成员之间穿插着非位域成员,那么不会进行压缩。
1
2
3
4
5
struct data1{
unsigned m: 12;
unsigned ch;
unsigned p: 4;
};

在各个编译器下 sizeof 的结果都是 12。

案例2:

  • 位段不能取地址:系统给内存中一个字节分配一个地址,位没有一个字节,没有地址
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include<stdio.h>
    struct data1{
    unsigned char a:2;
    unsigned int b:2;
    };
    int main(int argc,char *argv[]){
    struct data1 z;
    printf("%d\n",&z.a);
    return 0;
    }
    结果:
    图片

编译器会报错,不能对位的变量取地址

案例3:

  • 位段的赋值不要超过位段的大小,
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include<stdio.h>
    struct data1{
    unsigned char a:2;
    unsigned char b:2;
    unsigned char c:2;
    };
    int main(int argc,char *argv[]){
    struct data1 z;
    z.a=3;//11
    z.b=4;//100
    z.c=5;//101

    printf("%#x\n",z.a);
    printf("%#x\n",z.b);
    printf("%#x\n",z.c);
    return 0;
    }
    运行结果:
    图片

分析:如果超过位段的大小,则会取后面的值,前面的被截去了,显然这个没有意义的

案例4:

  • 无意义的位段 :2占有两位
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #include<stdio.h>
    struct data1{
    unsigned char a:2;
    unsigned char :5;
    unsigned char c:2;
    };
    struct data2{
    unsigned char a:2;
    unsigned char :4;
    unsigned char c:2;
    };
    int main(int argc,char *argv[]){
    printf("%d\n",sizeof(struct data1));//2
    struct data2 z;
    memset(&z,0,sizeof(z));
    //1100 0010 0xc2
    z.a=2;//10
    z.c=3;//11
    unsigned char *p=&z;
    printf("%#x\n",*p);

    return 0;
    }

运行结果:
图片

案例5:

  • 另起一个位段 :0 使下一个位段从下一个存储单元开始存放
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include<stdio.h>
    struct data1{
    unsigned char a:2;
    unsigned char :0;
    };
    struct data2{
    unsigned char a:2;
    unsigned char :0;
    unsigned char c:2;
    };
    struct data3{
    unsigned char a:2;
    //unsigned char :0;
    unsigned char c:2;
    };
    int main(int argc,char *argv[]){
    printf("%d\n",sizeof(struct data1));
    printf("%d\n",sizeof(struct data2));
    printf("%d\n",sizeof(struct data3));
    return 0;
    }

运行结果:
图片

注意:不会立马生效。是从下一个位段开始,在下一个存储单元存放,所以struct data1的字节数仍是1