结构体浅探
定义以下结构体:
typedef struct {
uint8_t s1 : 4;
uint8_t s2 : 4;
uint8_t s3 : 4;
uint8_t s4 : 4;
}SS;
该结构体占2字节
由于内存对齐:
typedef struct {
uint8_t s1 : 4;
}SS;
和
typedef struct {
uint8_t s1 : 4;
uint8_t s2 : 4;
}SS;
都占1字节
则s1-s4各占4 bit,
结构体不初始化:
SS s ;//vs2019编译通过,运行通过。内存信息1100 1100 1100 1100 1100 1100
结构体初始化:
SS s = {0};//vs2019编译通过,运行通过。内存信息0000 0000 0000 0000 1100 1100
SS s = {0,0};//vs2019编译通过,运行通过。内存信息0000 0000 0000 0000 1100 1100
SS s = {0,0,0};//vs2019编译通过,运行通过。内存信息0000 0000 0000 0000 1100 1100
SS s = {0,0,0,0,0};//vs2019编译未通过!!!!!初始值设定项值太多
SS s = {0x11,0x12,0x13};//vs2019编译通过,运行通过。内存信息0010 0001 0000 0011(0x21 0x03)
结论:vs2019中未初始化内存均为0xcc,只要执行初始化,没有预设值均默认0x00,
对于uint8_t s1 : 4;赋值时相当于执行了 &0x0F
赋值:
s.s1 = 0x01;
s.s2 = 0x02;
s.s3 = 0x03;
s.s4 = 0x04;
uint8_t *ss;
ss = (uint8_t *)&s;
打印输出:
printf(“size:%d\ndata:%x\n%x”,sizeof(s),ss[0],ss[1]);
内存排列情况:
0010 0001 0100 0011
s2 s1 s4 s3
***C语言指针支持类似数组下标索引(ss[0],ss[1]),其长度与(uint8_t *)指针类型相关***
将uint8_t改为uint16_t
typedef struct {
uint16_t s1 : 4;
uint16_t s2 : 4;
uint16_t s3 : 4;
uint16_t s4 : 4;
}SS;
结果不变
性能测试:
结构体:
s.s1 = 0x11;
s.s2 = 0x12;
s.s3 = 0x13;
s.s4 = 0x14;
对比数组操作(这里控制在四行)
str_1[0] = 0x11 & 0x0F;
str_1[0] += (0x12 & 0x0F) << 4;
str_1[1] = 0x13 & 0x0F;
str_1[1] += (0x14 & 0x0F) << 4;
循环执行100000000次
更不用说写成两行了
str_1[0] = 0x11 & 0x0F + ((0x12 & 0xFF) << 4);
str_1[1] = 0x13 & 0x0F + ((0x14 & 0xFF) << 4);
查看结构体赋值的汇编代码:
s.s1 = 0x11;
s.s2 = 0x12;
s.s3 = 0x13;
s.s4 = 0x14;
从汇编代码可以看出,操作流程是先取出变量所在字节,与操作清除对应4bit,或操作存入数据,编译器针对4位的结构体有所优化,在或运算时直接操作位移好的常量。
对于:
str_1[0] = (0x11 & 0x0F) + ((0x12 & 0x0F) << 4);
str_1[1] = (0x13 & 0x0F) + ((0x14 & 0x0F) << 4);
更加直接,编译器直接计算好进行赋值
将常量改为变量看看:定义一个数组:
uint8_t num[] = { 0x11,0x12,0x13,0x14 };
s.s1 = num[0];
s.s2 = num[1];
s.s3 = num[2];
s.s4 = num[3];
str_1[0] = (num[0] & 0x0F) + ((num[1] & 0x0F) << 4);
str_1[1] = (num[2] & 0x0F) + ((num[3] & 0x0F) << 4);
执行结果(相比常量稍慢一些):
查看汇编代码(34行):
结构体
s.s1 = num[0];
s.s2 = num[1];
s.s3 = num[2];
s.s4 = num[3];
相比赋值常数,仅仅多出一步取num的值。
数组(26行)
str_1[0] = (num[0] & 0x0F) + ((num[1] & 0x0F) << 4);
str_1[1] = (num[2] & 0x0F) + ((num[3] & 0x0F) << 4);
对比以上代码,就性能而言,使用位运算符相比结构体4位的储存方式略显优势,其他使用方式未测试。