面试中兴的时候,被问到了位段的内容,当时对位段毫不了解,今天就来个总结吧。
首先,位段是结构体为了节省内存的一种定义方式,在计算机网络中应用比较多,以下举例说明。
比如,我们现在有三个整形变量,变量的范围分别为0~15,0~10,0~254,我们知道 unssingned char表示的数字范围为0~254,所以,我们可以用三个unsigned char类型的成员来保存这三个变量,定义如下:
struct S { unsigned char val1; unsigned char val2; unsigned char val3 ; };我们可以计算出上述结构体的内存占用大小为3字节,共24位,我们知道15只需要4位就可以表示,10也只需要4位就可以表示,所以,诞生了位段,上述结构体的代码我们可以定义如下,并输出其内存大小:
#include <stdio.h> struct S { unsigned char val1 : 4; unsigned char val2 : 4; unsigned char val3 ; }; int main() { struct S s; s.val1 = 15; s.val2 = 10; s.val3 = 127; printf("sizeof(struct S) = %d \n", sizeof(struct S)); printf("s.val1 = %d \n", s.val1); printf("s.val2 = %d \n", s.val2); printf("s.val3 = %d \n", s.val3); return 0; }
上述代码运行结果为:
通过位段的定义,结构体的大小由原先的3字节节省到2字节,通过赋值验证,上述定义完全可以实现变量的需求。在计算机网络中,TCP的报文封装应用到了位段。
位段是将一变量类型,通常为char, int, unsigned int , insigned char 类型分成多位来操作,如unsigned int 类型可以分成32位,但是,C语言没有规定32位从左往右取,还是从右往左取,所以使用位段需要注意移植问题,位段使用尽量不要跨平台。
提到位,还有一个比较重要的考察知识点就是大小端问题,大端存储:就是低字节内容存放在高地址处,高字节内容存放在低地址处。也就是低对高,高对低。小端模式则刚好相反。
如:int val = 0x11223344;
如果为小端存储模式,内存中的字节存储方式为 44 33 22 11(从左到右地址由低到高)如果为大端存储模式,内存中的存储方式为11 22 33 44。通常,X86架构为小端存储模式,网络中的通常使用大端模式,所以在Linux系统中,网络编程处理数据需要大小端转换,Linux内核也集成了大小端转换函数ntohl()和htonl()。下面通过一个实例来说明大小端存储模式:
#include <stdio.h> int main() { int val1 = 0x11223344; char val2 = (char)val1; printf("%x\n", val2); return 0; }
代码运行结果为:
上述代码说明我的编译器为小端存储,对于整形数的4字节存储方式为44 33 22 11 ,低地址存储0x44,所以,将valu1赋值给val2的char类型只取了低字节,答案为0x44,通常也可以使用这种方式来判断当前机器为大端存储,或者小端存储,以下给出完整验证代码:
#include <stdio.h> int main() { int val1 = 1; if (1 == *(char*)(&val1)) { printf("小端模式\n"); } if (0 == *(char*)(&val1)) { printf("大端模式\n"); } return 0; }
当然也可以通过公用体来进行验证,下面代码说明:
#include <stdio.h> union UN{ int val1; char ch; }; int main() { union UN un; un.val1 = 1; if (1 == un.ch) { printf("小端模式\n"); } else if (0 == un.ch) { printf("大端模式\n"); } return 0; }
代码运行结果:
因为平常写代码的时候,涉及到优先级问题的时候,通常都可以用括号解决,当时面试的时候忽然被问起,搞的很懵,所以特别强调以下,位运算的优先级:~ > & > ^ > | ,也就是非的运算级大于与大于异或大于或。逻辑运算符的优先级顺序也是如此,即:!> && > ||
我们通过位运算来实现大小端的转换:
#include <stdio.h> int main() { int val = 0x11223344;//小端存储模式,从低到高地址,内存中单字节内存中存放的内容为44 33 22 11 int convertVal = 0; convertVal = ((val & 0xff) << 24) //取低8位,左移16位,按位&的优先级低于左移运算符的优先级,所以一定要加括号 | ((val & 0xff00) << 8) //取次低8位,左移8位 | ((val & 0xff0000) >> 8) //取次高8位,右移8位 | ((val & 0xff000000) >> 24);//取高8位,右移16位 printf("convertVal = 0x%x",convertVal); return 0; }
代码运行结果为: