学渣的C/C++

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

1、位段

面试中兴的时候,被问到了位段的内容,当时对位段毫不了解,今天就来个总结吧。

首先,位段是结构体为了节省内存的一种定义方式,在计算机网络中应用比较多,以下举例说明。

比如,我们现在有三个整形变量,变量的范围分别为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位从左往右取,还是从右往左取,所以使用位段需要注意移植问题,位段使用尽量不要跨平台。

2、大小端问题

提到位,还有一个比较重要的考察知识点就是大小端问题,大端存储:就是低字节内容存放在高地址处,高字节内容存放在低地址处。也就是低对高,高对低。小端模式则刚好相反。

如: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;
}

代码运行结果:

 3、位运算优先级

因为平常写代码的时候,涉及到优先级问题的时候,通常都可以用括号解决,当时面试的时候忽然被问起,搞的很懵,所以特别强调以下,位运算的优先级:~  > & > ^ > | ,也就是非的运算级大于与大于异或大于或。逻辑运算符的优先级顺序也是如此,即:!> && > ||

 我们通过位运算来实现大小端的转换:

#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;
        
}

代码运行结果为:

 

 

 

posted on 2021-10-30 22:42  学渣的C/C++  阅读(518)  评论(0编辑  收藏  举报