首页 > 其他分享 >Qt 大端模式、小端模式及其转换

Qt 大端模式、小端模式及其转换

时间:2024-03-13 09:14:08浏览次数:30  
标签:小端 存储 Qt -- 模式 大端 字节

  大端模式和小端模式是计算机中经常涉及到的两种字节序,也有大端对齐、小端对齐、大尾、小尾等叫法。

一、起源

  说起这两种模式,就不得不提一下大端(Big-endian)和小端(Little-endian)这两个英文上的起源。

  “endian”一词来源于乔纳森·斯威夫特的小说格列佛游记。Lilliput和Blefuscu这两个强国在过去的36个月中一直在苦战。战争的原因:大家都知道,吃鸡蛋的时候,原始的方法是打破鸡蛋较大的一端(Big-End),可以那时的皇帝的祖父由于小时侯吃鸡蛋,按这种方法把手指弄破了,因此他的父亲,就下令,命令所有的子民吃鸡蛋的时候,必须先打破鸡蛋较小的一端(Little-End),违令者重罚。然后老百姓对此法令极为反感,期间发生了多次叛乱,其中一个皇帝因此送命,另一个丢了王位,产生叛乱的原因就是另一个国家Blefuscu的国王大臣煽动起来的,叛乱平息后,就逃到这个帝国避难。据估计,先后几次有11000余人情愿死也不肯去打破鸡蛋较小的端吃鸡蛋。这个其实讽刺当时英国和法国之间持续的冲突。其中两种方法吃鸡蛋的人分别被称为Big-endians和Little-endians。1980年,Danny Cohen在其著名的论文”On Holy Wars and a Plea for Peace”中,为平息一场关于字节该以什么样的顺序传送的争论,而引用了该词。

二、存储模式

  接下来就说说为什么会有字节序的问题。

  计算机在存储数据的时候,是以字节(byte)为基本单位来存储的,因此存储单字节类型的数据(比如char)不存在字节序的问题。但存储多字节的数据的时候(比方说4字节的int变量),就涉及到了以一个什么样的顺序来存储。下面举例来说明大端和小端的存储方式。

  定义变量 unsigned long long a=0x1122334455667788

  变量a是一个64位的无符号整数,共需要8个字节来存储,那么在两种模式下是如何存储的呢?

||–1--||–2--||–3--||–4--||–5--||–6--||–7--||–8--|| 地址

|| 11 || 22 || 33 || 44 || 55 || 66 || 77 || 88 || 大端模式

|| 88 || 77 || 66 || 55 || 44 || 33 || 22 || 11 || 小端模式

  从中很容易可以看出各自的存储特点。

三、需要注意的几个问题

1.大端模式和小端模式是以基本类型为单位的

  对于long long a 和 struct{ char a;short b;int c;}二者同样占据了8个字节的空间,在存储上,前者上面已经介绍,后者则是先存储一个char,空一个字节,然后按照大端/小端模式存储short,最后按照大端/小端模式存储int。

2.大端模式与小端模式的实际应用范围

  在我们日常使用的x86架构的计算机中(其他类别的可能会采用大端模式或可配置模式,可以通过查阅资料或者用下文的代码进行测试),都是使用的小端模式,而网络字节序是大端模式的。这就使得在网络通信时进行字节序的转换变得极为重要。比方说,通信双方规定了了通信头为一个4字节的魔数(Magic Number),而一方按着大端序的模式发送,一方按着小端序的模式解读,那么两方的通信就会失败。如果没有这个魔数,而在内部的数据中出现这样的问题则会更加的麻烦。

3.文件存储中的模式

  文件的存储一般都是以字节来进行操作的,因此,在文件中以什么样的字节序需要程序的编写者加以注意。比方说下面的程序:

1 int a=0x11223344;
2 FILE *fp;
3 fp=fopen("test","wb");
4 fwrite(&a,sizeof(a),1,fp);
5 fclose(fp);

  用十六进制编辑器打开文件之后,我们会发现文件的内容是44332211。原因很简单,fwrite函数直接把内存中的内容按顺序写入了文件,因此内存中是小端模式存储的,所以写入文件也是小端模式。
四、优缺点

  大端模式,由于符号位和数值的高位存在地址的低位,会优先被读到,更容易先确定数据的重要信息。

  小端模式,在进行类型转换的时候不需要调整数据。如int强制转换到char,计算机不需要做任何调整,直接读取int的第一个字节即可。

五、大端和小端的检测

  对于大端模式和小端模式的检测,可以利用上面所说的强制类型转换。

1 bool isLittleEndian()
2 {
3     short a=0x0061;
4     if((char)a=='a') return true;
5     else return false;
6 }

  在查阅资料后,还发现了另外一个方法,利用到了被我遗忘很久的一个数据结构,联合体union。

 1 bool isLittleEndian()
 2 {
 3     union
 4     {
 5         short a;
 6         char  b;
 7     }test;
 8     test.a=0x0061;
 9     if(test.b=='a') return true;
10     else return false;
11 }

  这个方法利用了联合体共用内存的特性,因此回避了强制类型转换。

六、Qt中大端小端的转换

  Qt中包含了大端小端转换的几个函数

1 T    qFromBigEndian(const uchar * src)
2 T    qFromBigEndian(T src)
3 T    qFromLittleEndian(const uchar * src)
4 T    qFromLittleEndian(T src)
5 void    qToBigEndian(T src, uchar * dest)
6 T    qToBigEndian(T src)
7 void    qToLittleEndian(T src, uchar * dest)
8 T    qToLittleEndian(T src)

  下面对这个几个函数进行简单的说明。

1 union{
2     int a;
3     char b[4];
4 }test1,test2;
5 test1.a=0x61626364;
6 test2.a=qFromBigEndian(test1.a);
7 qDebug()<<test1.b[0]<<test1.b[1]<<test1.b[2]<<test1.b[3];
8 qDebug()<<test2.b[0]<<test2.b[1]<<test2.b[2]<<test2.b[3];

  对于qFromBigEndian()函数,它会判断执行程序的主机的字节序,如果是大端模式的计算机,那么只是读取数据,不进行转换,如果是小端模式的计算机,那么则进行转换。
  因此我在本机(小端模式)上的的执行结果是:

1 d c b a
2 
3 a b c d

  可以看出,它将数据进行了转换。
  对于qFromLittleEndian()函数,和前者类似。对于大端模式的计算机进行转换,对于小端模式的计算机只是读取数据。

1 union{
2     int a;
3     char b[4];
4 }test1,test2;
5 test1.a=0x61626364;
6 test2.a=qFromLittleEndian(test1.a);
7 qToBigEndian(test1.a,(uchar*)test2.b);
8 qDebug()<<test1.b[0]<<test1.b[1]<<test1.b[2]<<test1.b[3];
9 qDebug()<<test2.b[0]<<test2.b[1]<<test2.b[2]<<test2.b[3];

  对于qToBigEndian()函数,也有着上面的规则,对于小端模式的计算机进行转换,对于大端模式的计算机只进行读取。
  因此,本机(小端模式)的执行结果是:

1 d c b a
2 
3 a b c d

  对于qToLittleEndian()函数,只对大端模式的计算机进行转换。

  需要注意的是,Qt中的模板T只针对有符号和无符号的整型,对于浮点型(一般也不会用到),需要进行强制类型转换。

标签:小端,存储,Qt,--,模式,大端,字节
From: https://www.cnblogs.com/ybqjymy/p/18069781

相关文章

  • Qt QTextStream 类(文本流)和 QDataStream 类(数据流)
    一、二者区别(1)QTextStream类:用于对数据进行文本格式的读/写操作,可在QString、QIODevice或QByteArray上运行,比如把数据输出到QString、QIODevice或QByteArray对象上,或进行相反的操作。(2)QDataStream类:用于对数据进行二进制格式的读/写操作,QDataStream只可在QIOD......
  • mac redis启动,redis哨兵模式,redis集群的相关命令
    Homebrew安装的软件会默认在/usr/local/Cellar/路径下redis的配置文件redis.conf存放在/usr/local/etc路径下cd/usr/local/Cellar/redis/7.0.10.存在cd/usr/local/opt/redis/bin/redis-server.目录存在cd/usr/local/etc/redis.conf存在。配置文件复制文件cp/us......
  • 错误:在 /tmp/easy_install-rad8_t5b/PyQt5-5.14.0.tar.gz #15 中找不到安装脚本
    thePyQt55.14.0isbrokenbecausecan'tnotinstallonresppi3.youcantoinstallaversionofPyQt5thatworkingfineonresp.followthesteps:PyQt55.14.0已损坏,因为无法无法安装在resppi3上。您可以安装一个在resp上运行良好的PyQt5版本,请按照以下......
  • 设计模式 -- 1:简单工厂模式
    目录代码记录代码部分代码记录设计模式的代码注意要运用到面向对象的思想考虑到紧耦合和松耦合把具体的操作类分开不让其互相影响(注意这点)下面是UML类图代码部分#include<iostream>#include<memory>//引入智能指针的头文件usingnamespacestd;......
  • QT生成固定长度的随机字符串
    最近项目中有一个需要使用QT生成固定长度随机字符串的需求,需求也很简单,就是生成一个n位的仅包含0-9以及大写字母的字符串,因为这也是第一次使用QT自身的随机数,这里就做一下简单记录。废话不多说,直接上代码。1QStringgetRandomString(intlength)2{3qsrand(QDateTi......
  • 基于QT实现的微量气体数据记录软件
      最近收到一个要求实现力高泰的微量气体分析仪数据下载与记录软件的需求。需要支持力高泰LI-7810、LI-78150、LI-7820、LI-850/830等微量气体分析仪,另外还包括配套的阀箱、冷阱及采样单元的数据获取与记录。其中LI-7810、LI-78150、LI-7820采用以太网通讯,应用成蓄意为MQTT;LI-85......
  • 2、策略模式
    C++抽象类(接口)一、接口:描述类的行为和功能,而无需完成类的特定实现C++接口通过抽象类实现的,设计抽象类的目的,是为了给其他类提供一个可以继承的适当的基类;如果派生类没有重写所有纯虚函数,则派生类也是抽象类(抽象派生类);派生类只有实现所继承的抽象基类中的所有纯虚......
  • 单例模式的几种实现方式
    单例模式的实现有多种方式,如下所示:1、懒汉式,线程不安全是否Lazy初始化:是是否多线程安全:否实现难度:易描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁synchronized,所以严格意义上它并不算单例模式。这种方式lazyloading很明显,不要......
  • 工业智能网关的网关模式和交换机模式有哪些区别
    工业智能网关主要用以实现工业物联网络中不同设备和系统之间的通信,工业智能网关常见的两种工作模式是网关模式和交换机模式,在功能和应用场景上存在显著差异,本篇就为大家简单介绍一下两者之间的主要区别: 1、网关模式工业智能网关的网关模式可以实现不同网络和设备之间的数据交......
  • Qt QByteArray与int、float、vector互转
    QByteArray的转换一般在串口通信中常用,将int、float、double转换为4个字节的数组(如255->[0,0,0,255],本文章一律用10进制表示),或将4个字节的数组转换为int、float、double(如[0,0,0,255]->255)。在QT界面中的使用流程,笔者总结为以下思路:从上位机发送一帧指令到串口(封包、根据具体......