字节序(byte order)和位序(bit order)

2023-05-16

字节序(byte order)和位序(bit order)

 在网络编程中经常会提到网络字节序和主机序,也就是说当一个对象由多个字节组成的时候需要注意对象的多个字节在内存中的顺序。
 以前我也基本只了解过字节序,但是有一天当我看到ip.h中对IP头部结构体struct iphdr的定义时,我发现其中竟然对一个字节中的8个比特位也区分了大小端,这时我就迷糊了,不是说大小端只有在多个字节之间才会有区分的吗,为什么这里的定义却对一个字节中的比特位也区分大小端呢?
 下面我们先看一下struct iphdr的定义,后文会解惑为什么要在一个字节中区分大小端。

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
    __u8    ihl:4,
        version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
    __u8    version:4,
        ihl:4;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
    __u8    tos;
    __be16  tot_len;
    __be16  id;
    __be16  frag_off;
    __u8    ttl;
    __u8    protocol;
    __sum16 check;
    __be32  saddr;
    __be32  daddr;
    /*The options start here. */
};
  1. 字节序(Byte order)
     关于字节序的文章已经有很多了,在我这篇文章中不打算过多的说字节序,但是也不能完全脱离字节序因为后面的重点部分比特序跟字节序也有一定的相似度和联系。
     字节序就是说一个对象的多个字节在内存中如何排序存放,比如我们要想往一个地址a中写入一个整形数据0x12345678,那么最后在内存中是如何存放这四个字节的呢?
     0x12这个字节值为最高有效字节,也就是整数值的最高位(在本文中0x12=0x12000000),0x78为最低有效字节。
    这里写图片描述
                图1:大端字节序
    上图是大端字节序的示意图,所谓”大端字节序”,便是指最高有效字节落在低地址上的字节存放方式。
    这里写图片描述
                图2:小端字节序
     而小端字节序就是最低有效字节落在低地址上的字节存放方式。
     0x12345678=0x12000000 + 0x340000 + 0x5600 + 0x78,所以要想保持一个对象的值在大小端系统之间不变,那么就必须确保不同的系统能够正确的识别最高有效字节和最低有效字节(不能错误的识别最高、最低有效字节)。
     同样的字节序12 34 56 78在大端序机器中会识别为0x12345678(0x12000000 + 0x340000 + 0x5600 + 0x78=0x12345678),在小端序机器中识别为0x78563412(0x12 + 0x3400 + 0x5600 00+ 0x78000000=0x78563412)。
     所以要想两者保持一致就必须确保系统能够正确的识别最高有效字节0x12和最低有效字节0x78,那么在小端系统中字节存放的顺序应该为78 56 34 12。
  2. 比特序(bit order)
     字节序是一个对象中的多个字节之间的顺序问题,比特序就是一个字节中的8个比特位(bit)之间的顺序问题。一般情况下系统的比特序和字节序是保持一致的。
     一个字节由8个bit组成,这8个bit也存在如何排序的情况,跟字节序类似的有最高有效比特位、最低有效比特位。
     比特序1 0 0 1 0 0 1 0在大端系统中最高有效比特位为1、最低有效比特位为0,字节的值为0x92。在小端系统中最高、最低有效比特位则相反为0、1,字节的值为0x49。
     跟字节序类似,要想保持一个字节值不变那么就要使系统能正确的识别最高、最低有效比特位。
  3. 字节序转换函数ntohl(s)、htonl(s)
     在socket编程中经常要用到网络字节序转换函数ntohl、htonl来进行主机序和网络序(大端序)的转换,在主机序为小端的系统中字节序列78 56 34 12(val=0x12345678)经过htonl转换后字节序列变成12 34 56 78:
    这里写图片描述
                图3:htonl函数
     字节序转换后我在想是不是比特序也一同进行了转换?
     为什么会有这个疑问呢,因为前文可知系统的比特序和字节序是一致的,现在字节序已经从小端变成了大端那么比特序应该也要一起转换。而且如果比特序不变化那么当这些字节到了目标大端序系统中后每一个字节的值都会发生变化,因为同样的比特序列在小端和大端系统中识别的字节值会不一样。
     首先从htonl、ntohl的源码来看确实只进行了字节序的转换并没有进行比特序的转换,再有就是以前socket编程的时候只调用了ntohl、htonl等函数并没有调用(而且系统也没有提供)比特序转换函数,但是最后的结果都是正确的,并没有发现上面提到的字节值发生变化的问题。
     那么这个”神奇”的事情是怎么解决的呢,好像系统本身就给我们”悄悄”的解决了我担心的问题。
    答案我们下文揭晓。
  4. 比特(bit)的发送和接收顺序
     比特的发送、接收顺序是指一个字节中的bit在网络电缆中是如何发送、接收的。在以太网(Ethernet)中,是从最低有效比特位到最高有效比特位的发送顺序,也就是最低有效比特位首先发送,参考资料:frame。
     在以太网中这个规定有点奇怪,因为字节序我们是按照大端序来发送,但是比特序却是按照小端序的方式来发送,下图是直接从网上找来的一张图,主机序本身是大端序:
    这里写图片描述
                图4:比特发送、接受示意图
     比特的发送、接收顺序对CPU、软件都是不可见的,因为我们的网卡会给我们处理这种转换,在发送的时候按照小端序发送比特位,在接收的时候会把接收到的比特序转换成主机的比特序,下面是一个小端机器发送一个int整型给一个大端机器的示意图:
    这里写图片描述
                图5:小端->大端比特发送示例
     因为对网卡对比特序的发送、接收所做的转换没有深入的了解所以上图很有可能会有错误之处。
     现在来回答一下第3节中的那个疑问:
    • htonl、ntohl函数肯定是不会同步转换一个字节中的比特序的,因为如果比特序也发生了转换的话那么这个字节的值也就发生了变化,记住htonl、ntohl只是字节序转换函数。
    • 比特序按照小端的方式发送,首先发送的是最低有效比特位,最后发送的是最高有效比特位,接收端的网卡在接收到比特序列后按照主机的比特序把接收到的”小端序”比特流转换成主机对应的比特序列。
       可以假设存在ntohb、htonb(b代表bit)这样的两个函数,网卡进行了比特序的转换,不过是这两个函数是网卡自动调用的,我们平时不用关注。
    • 按照规则,发送、接收的时候进行比特序的转换,那么就能保证在不同的机器之间进行通信不会发生我担心的字节值发生变化的问题。
  5. 结构体的位域
     关于C语言中结构体的位域可以参考这篇文章:http://tonybai.com/2013/05/21/talk-about-bitfield-in-c-again/,对于位域的具体用法、语法参考这篇文章即可有。
     对于位域有一个约定:在C语言的结构体中如果包含了位域,如果位域A定义在位域B之前,那么位域A总是出现在低序的比特位。
    在计算机中可寻址的最小单位为字节,bit是无法寻址的,但是为了抽象我们可以把计算机的最小寻址单位变成bit,也就是我们可以单独获得一个bit位。
     我们有如下的一段代码:
#include<stdio.h>

struct bit_order{
    unsigned char a: 2,
                  b: 3,
                  c: 3;
};

int main(int argc, char *argv[])
{
    unsigned char ch      = 0x79;
    struct bit_order *ptr = (struct bit_order *)&ch;

    printf("bit_order->a : %u\n", ptr->a);
    printf("bit_order->b : %u\n", ptr->b);
    printf("bit_order->c : %u\n", ptr->c);

    return 0;
}

 我们把代码在gentoo(intel小端机器)、hu-unix(大端机器)两个机器上面编译、运行,结果如下:

liuxingen@ V6-Dev ~/station $ ./bitfiled
bit_order->a : 1
bit_order->b : 6
bit_order->c : 3
下面是hp-unix的运行结果
# ./bitfiled
bit_order->a : 1
bit_order->b : 7
bit_order->c : 1

 我们先分析一下gentoo上面的结果:
这里写图片描述
            图6:小端机器的位域示例
 从上图中我们很容易就能理解gentoo上面的输出结果,下面是hp-unix上面示意图:
这里写图片描述
            图7:大端机器的位域示例
 从上面的输出可以看到同样的代码在不同的机器中输出了不同的结果,也就是说我们的代码在不同的平台不能直接移植,导致这个问题的原因就是我们前面提到的关于位域的一个约定,定义在前面的位域总是出现在低地址的bit位中,因为不同的平台的比特序是不同的,但是我们定义的位域没有根据平台的大小端进行转换,最后就导致了问题。那么如何解决这个问题,那就是在定义结构体中的位域时判断平台的大小端:

#include<stdio.h>
#include<asm/byteorder.h>

struct bit_order{
#if defined(__LITTLE_ENDIAN_BITFIELD)
    unsigned char a: 2,
                  b: 3,
                  c: 3;
#elif defined (__BIG_ENDIAN_BITFIELD)
    unsigned char c: 3,
                  b: 3,
                  a: 2;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
};

int main(int argc, char *argv[])
{
    unsigned char ch      = 0x79;
    struct bit_order *ptr = (struct bit_order *)&ch;

    printf("bit_order->a : %u\n", ptr->a);
    printf("bit_order->b : %u\n", ptr->b);
    printf("bit_order->c : %u\n", ptr->c);

    return 0;
}

 到此我们也就解释了文章开头关于struct iphdr定义中的那个疑问。
 最后给大家隆重介绍一篇文章,对我启发很大,文中的很多知识来自于它:byte order and bit order

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

字节序(byte order)和位序(bit order) 的相关文章

  • bmi055六轴传感器获取数据

    BMI055的加速度计和陀螺仪的地址是分开的 xff0c 在读取的时候要分开读取 xff0c 我是用的iic的方式读取的 define ACC ADDRESS 0X18 加速度计的地址 define GYRO ADDRESS 0X68 陀螺
  • 使用arecord、aplay命令实现音频的采集和播放

    arecord和aplay是alsa utils一部分 我们在Linux系统下进行音频开发时经常使用 xff0c 非常方便 先简单介绍一下 arecord 采集原始音频 arecord r 8000 t raw c 1 f S16 BE t
  • centos7安装docker与删除容器实例和镜像

    doker简介 docker是一个开源的应用容器引擎 xff0c 让开发者可以打包他们的应用以及依赖包到一个可移植的容器中 xff0c 然后发布到任何流行的linux机器或者windows机器上 xff0c 也可以实现虚拟化 容器是完全使用
  • 刘韧:记者的数字力量

    本文写于2003年09月04日 真正到了收拾物品离开报社的那一刻 xff0c 他才确切感到空落来得如此地具体 从明天起 xff0c 他的稿件将不能再见诸本报 xff1b 从明天起 xff0c 他将失去本报读者 xff1b 从明天起 xff0
  • 刘韧:此时使用文字 只因为文字简练

    以下文字写于2007年 一 此时使用文字只因为文字简练 1 博士说他的女儿有阅读障碍 我说 xff0c 我10多岁的时候 xff0c 也有阅读障碍 我爸让我读杂志上的一篇文章 xff0c 读完 xff0c 问我这篇文章在讲什么 xff0c
  • 刘韧:角色、扮相、知识与历史

    以下文字皆写于2008年1月 一 角色与扮相的欺骗 1 当编辑时 xff0c 最怕向名家约稿 xff0c 名家赏脸写的稿子 xff0c 质量不高 xff0c 不能用 2 喜欢李白的人 xff0c 千万不要买 李白全集 xff0c 全集里有许
  • 刘韧:和人物共同创作人物故事

    编者按 xff1a 本文为DoNews编辑部内训课实录 xff0c 创作于2011年 由传媒见闻谭缘于2020年4月根据录音整理 我从1997年开始人物写作 xff0c 一直写到2003年 5年间 xff0c 无论刮风下雨 xff0c 还是
  • 刘韧马杰花总:诗歌小说电影游戏都是元宇宙

    时间 xff1a 2021年10月31日晚 访谈 xff1a 花总 xff08 网络红人 xff0c 被 华盛顿邮报 称为 在风险中推动变革的博客 代表 xff09 嘉宾 xff1a 刘韧 xff08 中国著名IT记者 xff09 马杰 x
  • 刘韧:怎样做记者

    编发按 xff1a 2021年11月27日 xff0c CSDN刘韧写作班第一期课后 xff0c 潜山同学说 xff1a 2001年 xff0c 我爸说你给他们培训 xff0c 主题是 怎样做记者 xff0c 他把你当时培训的内容打印出来
  • 尤雨溪Vue登榜GitHub之路看似不难

    本文完成于2022年3月6日 xff0c CSDN首发 xff0c 将在 新程序员 杂志刊登 采访撰稿 xff1a 刘韧 谷磊 林兴陆 李彤等 录音整理 xff1a 谷磊 周扬 林兴陆 鲁飞龙 编辑校对 xff1a 田玮靖 萧少聪 王雪艳
  • ROS:关于xacro模型在gazebo的加载

    ROS xff1a 关于xacro模型在gazebo的加载 这个模型加载问题折磨了我好几天 xff0c 今天总算是找到问题所在 我还一直以为是新版本的问题 xff0c 结果却是自己的问题 不够仔细 因此记录下来 xff0c 引以为戒 1 问
  • 刘韧:元宇宙不需要普通人

    作者 xff1a 刘韧 编辑 xff1a 谷磊 1 躲进小楼成一统 xff0c 我理解是 xff0c 躲进小圈子的小宇宙 xff0c 这个小宇宙基础如果是Web3 0 xff0c 那么就叫元宇宙了 自嘲 鲁迅 运交华盖欲何求 xff1f 未
  • 开源时代:刘韧对话任旭东崔宝秋章文嵩蒋涛

    来源 xff1a 1024程序员节 之 技术英雄会 主题 xff1a 开源英雄共话 我们的开源时代 时间 xff1a 2022年 10月 24日 主持嘉宾 刘韧 xff1a 云算科技董事长 知识英雄 作者 DoNews创始人 对话嘉宾 任旭
  • 刘韧工作手册(2023年版)

    刘韧于2022年9月22日为云算科技做内部演讲 由谭缘整理成文 xff0c 李欣欣编辑 xff0c 朱芳文审定 一 认知篇 01 干中学 xff0c 重复做 学 是为了 习 xff0c 学到的东西是为了下一次习的时候 xff0c 做得更好
  • 个人大于集体

    詹姆斯库克大学新加坡校舍正门 我依旧记得高中时发的一条朋友圈 xff1a 一个人的价值是由他周围的人决定的 十五岁时 xff0c 我一个人离开家乡 xff0c 来到新加坡 半年后 xff0c 把第一所学校的语言班老师骂退休了 xff0c 我
  • Foresight对话:刘韧对谈王建硕、曾映龙、Joy Xue

    Foresight 2023论坛现场 自 2022年 11月上线以来 xff0c OpenAI研发的ChatGPT一度风靡全球 面对这波 AI浪潮 xff0c 有些人拥抱了新趋势 xff0c 有些人则担心会被取代 xff0c 另一些人发掘其
  • 贾扬清开源 AI 框架 Caffe | 开源英雄

    编者按 在开源与人工智能的灿烂星河里 xff0c 贾扬清的名字都格外地耀眼 因为导师 Trevor Darrell 教授的一句 你是想多花时间写一篇大家估计不是很在意的毕业论文 xff0c 还是写一个将来大家都会用的框架 xff1f xff
  • 一个程序员的连续套现

    Fishman xff0c 吴锡桑 28岁 xff0c 中国软件行业协会理事 xff0c 1995年毕业于暨南大学计算机系 致力于多媒体和互联网软件的开发多年 xff0c 著作的软件曾获广东省 34 高校杯 34 软件比赛第一名 xff1b
  • 雷军留名

    影响中关村的50个人 知识英雄 Wednesday December 26 2001 3 29 PM 刘韧 雷军 xff0c 1969年2月16日出生于湖北省仙桃市 xff1b 1991年 xff0c 毕业于武汉大学计算机系 xff1b 1
  • docker load 是个什么东西?

    docker load 是个什么东西 xff1f docker load 是一个用于将 Docker 镜像加载到本地 Docker 环境中的命令 通常 xff0c 我们将 Docker 镜像从 Docker Hub 或者其他镜像仓库中下载到

随机推荐

  • Git同步一直转的解决方法

    之前遇到的一个问题 xff1a 使用VScode软件的Git同步不管怎样都无法拉取推送 xff08 左下角会一直转 xff0c 而且没有报错提示 xff09 但是在对应项目的文件目录下 xff0c 使用控制台就可以 在VSCode的控制台输
  • ROS:关于节点和节点句柄以及命名空间

    ROS xff1a 关于节点和节点句柄以及命名空间 参考资料 xff1a ROS官方文档 首先 xff0c 我们需要明确的是 节点 和 节点句柄 是不同的 一般而言 xff0c 一个cpp文件只能启动一个ROS节点 xff0c 但作为该节点
  • 【操作系统】生产者消费者问题

    生产者消费者模型 文章目录 生产者消费者模型 64 toc 一 生产者消费者问题二 问题分析三 伪代码实现四 代码实现 xff08 C 43 43 xff09 五 互斥锁与条件变量的使用比较 一 生产者消费者问题 生产者消费者问题 xff0
  • VScode中使用git终端,无法识别命令

    提示 xff1a vscode中使用git终端 xff0c 无法识别输入的命令 xff1a vscode版本 xff1a VSCodeUserSetup x64 1 60 1 exe git版本 xff1a 2 32 0 windows 2
  • Ubuntu18.04 VINS-Mono & Fast-Planner

    Ubuntu18 04 VINS Mono amp Fast Planner 官方GIthub 安装依赖 span class token comment 额外ros包 span span class token function sudo
  • Autoware Docker 安装

    1 Ubuntu20 04 Docker 官方教程安装 Docker 官方教程安装 2 安装 nvidia container runtime Access an NVIDIA GPU 官方参考 span class token comme
  • 卡尔曼滤波公式理解

    卡尔曼滤波 卡尔曼滤波适用于线性高斯系统 xff0c 即系统满足叠加性 齐次性 xff0c 噪声满足正态分布 其使用上一次的最优结果预测当前的值 xff08 先验估计 xff09 xff0c 同时使用观测值修正当前值 xff0c 得到最优结
  • 学习编程,API很重要么?

    学习编程 xff0c API的重要性几何 xff1f 在培训中 xff0c 很多人问到了 xff0c 学习Java xff0c 是否需要学习那些大量API的用法 xff0c 从而成为一个精通Java编程开发的coder xff1f 首先 x
  • App 后台架构设计方案 设计思想与最佳实践

    CSDN 2016博客之星评选结果公布 系列直播 零基础学习微信小程序 xff01 我的2016 主题征文活动 博客的神秘功能 App 后台架构设计方案 设计思想与最佳实践 标签 xff1a App后台架构设计用户验证方案后台架构的演进架构
  • 语音信号处理算法

    EQ DRC AEC NS
  • uIP与LWIP的区别

    uIP是专门为8位和16位控制器设计的一个非常小的TCP IP栈 完全用C编写 xff0c 因此可移植到各种不同的结构和操作系统上 xff0c 一个编译过的栈可以在几KB ROM或几百字节RAM中运行 uIP中还包括一个HTTP服务器作为服
  • C++基础用法—冒号的用法

    C 43 43 基础用法 冒号的用法 一 用法一 xff1a 继承1 1用法 xff1a 用在class或struct后面1 2示例代码 二 用法二 xff1a 赋值1 1用法 xff1a 用在成员函数 构造函数后面1 2示例代码1 2 1
  • 路由器相关开发流程

    路由器相关开发流程 宽带上网已经不是什么新鲜事 情 xff0c 人们对相关的网络器件已经不再陌生 xff0c 比如说常见的路由器 对于一般的网络用户 xff0c 他们能知道怎样使用路由器来上网 玩游戏等就已经感到很满足了 xff0c 通常情
  • 将Ubuntu终端的输出信息保存到log中

    参考website xff1a http www linuxidc com Linux 2011 04 34543 htm 执行以下命令 xff0c 开启保存 xff1a sudo script screen log 之后你在终端输入的相关
  • 《芯片介绍系列》之MT7628芯片详细介绍(MT7628平台处理器)

    有的家庭路由器拆开 xff0c 里面最显眼的芯片型号是 xff1a MT7628KN MT7628系列产品是新一代2T2R 802 11n Wi Fi AP 路由器 系统单芯片 xff08 摘自百度 xff09 更多介绍见此 xff1a M
  • python:函数传参是否会改变函数外参数的值

    参考链接 xff1a https blog csdn net u012614287 article details 57149290 https blog csdn net qq915697213 article details 66972
  • Catkin创建工程

    介绍 catin make编译过程 xff0c cmake指令依据你的CMakeLists txt 文件 生成cmakefiles和makefile文件 make再依据此cmakefiles和makefile文件编译链接生成可执行文件 ca
  • ros中使用serial包实现串口通信

    一 Ubuntu下的串口助手cutecom 1 安装cutecom并打开 xff1a sudo apt get install cutecom sudo cutecom 2 查看电脑链接的串口信息 xff08 名称 xff09 xff1a
  • 构造函数后冒号的作用

    构造函数后面的冒号起分割作用 xff0c 是类给成员变量赋值的方法 xff0c 初始化列表 xff0c 更适用于成员变量的常量const型 struct XXX XXX y 0xc0
  • 字节序(byte order)和位序(bit order)

    字节序 byte order 和位序 bit order 在网络编程中经常会提到网络字节序和主机序 xff0c 也就是说当一个对象由多个字节组成的时候需要注意对象的多个字节在内存中的顺序 以前我也基本只了解过字节序 xff0c 但是有一天当