Qt|大小端数据转换

news2024/11/20 1:28:10

后面打算写Qt关于网络编程的博客,网络编程就绕不开字节流数据传输,字节流数据的传输一般是根据协议来定义对应的报文该如何组包,那这就必然牵扯到了大端字节序和小端字节序的问题了。不清楚的大小端的可以看一下相关资料:大小端模式_百度百科 (baidu.com)。

这里看一个具体的例子比如某个报文协议是这样定的:

报头...
设备编号U16(两个字节)
设备温度U16(两个字节)
设备湿度U16(两个字节)
设备状态U16(两个字节)
报尾...

那么传输过程的报文结构,除去头尾之外应该是这样的:

代码报文结构体这样定义

struct DeviceData {
  quint16 number;
  quint16 temperature;
  quint16 humidness;
  quint16 status;
};

然后简单测试一下

  QByteArray data;
  DeviceData d;
  d.number = 0x1234;
  d.temperature = 0x5678;
  d.humidness = 0x4321;
  d.status = 0x8756;
  data.append(reinterpret_cast<char *>(&d), sizeof(DeviceData));
  qDebug() << data.toHex();

编译运行查看一下打印结果:

如果看了前面关于大小端的资料应该就会明白为什么这里打印结果是“3412785621435687”,这里也有一个参考的文章:大小端格式由编译器,操作系统还是CPU决定的?答案是CPU_大端cpu采用小端编译链-CSDN博客我的机器cpu,如果不采取任何处理,这里输出的确实是小端数据,如果是大端那么就会输出对应的“1234567843218756”。如果上文中举例的协议定的就是小端数据传输那么就是这样写,无需做任何处理。如果是大端数据传输则需要做对应的处理。同样上文中还存在一个问题,比如设备编号协议里面定的是无符号一个字节即uint8类型的。那么结构体将会这样定义:

struct DeviceData {
  quint8 number;
  quint16 temperature;
  quint16 humidness;
  quint16 status;
};

测试代码:

  QByteArray data;
  DeviceData d;
  d.number = 0x12;
  d.temperature = 0x5678;
  d.humidness = 0x4321;
  d.status = 0x8756;
  data.append(reinterpret_cast<char *>(&d), sizeof(DeviceData));
  qDebug() << data.toHex();

这里将结构体转QByteArray使用了reinterpret_cast,可以自己查一下static_castdymatic_castreinterpret_cast以及Qt的qobject_cast有什么区别。同样还可以使用QByteArray的setRawData方法:

QByteArray data;
data.setRawData(reinterpret_cast<char *>(&d), sizeof(DeviceData));

结果也是一样的。

将对应QByteArray转回结构体直接使用memcpy即可。例如上面的例子:

DeviceData dd;

memcpy(&dd, data.constData(), sizeof(DeviceData));

打印输出:

为什么打印是这样,可以先看看这个结构体的大小,打印 sizeof(DeviceData)可以看到是8个字节,结构体成员一个quint8,三个quint16,大小:1+2+2+2为什么是8而不是7, 这个就需要了解关于字节对齐的知识了:字节对齐_百度百科 (baidu.com)

直接采用1字节对齐:

#pragma pack(push, 1);
struct DeviceData {
  quint8 number;
  quint16 temperature;
  quint16 humidness;
  quint16 status;
};
#pragma pack(pop);

再查看打印:

然后sizeof(DeviceData)也是7了(这里使用1字节对齐会影响效率)。·

回到关于大小端的问题,代码里面采用的是结构体转QByteArray,这样就牵扯到了依靠系统自己的大小端来处理了,代码不灵活。可以写一个通用的方法来根据需求转换对应需要的字节序。

方法一:

思路是一个字节一个字节进行拷贝使用QByteArray的append方法以及位移操作。

比如一个无符号四字节的quint32 u32=0x12345678(大端就是12345678,小端就是78563412),先执行下列代码:

  quint32 u32 = 0x12345678;
  QByteArray data;
  data.append(u32);
  qDebug() << data.toHex();

查看打印:

也就是说QByteArray的 append方法在这种情况下并不会将整数 u32 转换为字节流,而是将整数的低字节(最低有效字节)追加到 QByteArray中。

转化为小端数据:

原生数据操作原生数据QByteArray进行append追加
12345678右移0位1234567878
12345678右移8位001234567856
00123456右移16位00001234785634
00001234右移24位0000001278563412

转化为大端数据:

原生数据操作原生数据QByteArray进行append追加
12345678右移24位0000001212
12345678右移16位000012341234
00123456右移8位00123456123456
00001234右移0位1234567812345678

对应代码:

  quint32 u32 = 0x12345678;
  //输出小端数据
  QByteArray littleEndian;
  littleEndian.append(u32);
  littleEndian.append(u32 >> 8);
  littleEndian.append(u32 >> 16);
  littleEndian.append(u32 >> 24);
  qDebug() << "little:" << littleEndian.toHex();
  //输出大端数据
  QByteArray bigEndian;
  bigEndian.append(u32 >> 24);
  bigEndian.append(u32 >> 16);
  bigEndian.append(u32 >> 8);
  bigEndian.append(u32);
  qDebug() << "bigEndian:" << bigEndian.toHex();

编译运行查看打印:

 对应转回同理,下面是完整代码:

  quint32 u32 = 0x12345678;
  //输出小端数据
  QByteArray littleEndian;
  littleEndian.append(u32);
  littleEndian.append(u32 >> 8);
  littleEndian.append(u32 >> 16);
  littleEndian.append(u32 >> 24);
  qDebug() << "littleEndian:" << littleEndian.toHex();
  quint32 u32x = 0;
  u32x |= static_cast<quint8>(littleEndian[0]);
  u32x |= (static_cast<quint8>(littleEndian[1]) << 8);
  u32x |= (static_cast<quint8>(littleEndian[2]) << 16);
  u32x |= (static_cast<quint8>(littleEndian[3]) << 24);
  qDebug() << "ori data:" << u32x;
  //输出大端数据
  QByteArray bigEndian;
  bigEndian.append(u32 >> 24);
  bigEndian.append(u32 >> 16);
  bigEndian.append(u32 >> 8);
  bigEndian.append(u32);
  qDebug() << "bigEndian:" << bigEndian.toHex();
  quint32 u32y = 0;
  u32y |= (static_cast<quint8>(bigEndian[0]) << 24);
  u32y |= (static_cast<quint8>(bigEndian[1]) << 16);
  u32y |= (static_cast<quint8>(bigEndian[2]) << 8);
  u32y |= static_cast<quint8>(bigEndian[3]);
  qDebug() << "ori data:" << u32y;

编译运行查看打印:

305419896对应的16进制就是0x12345678:

其他数据类型同理,这里写成模板函数:

template <typename T>
static QByteArray toData(const T &value, bool isLittle) {
  QByteArray data;
  for (int i = 0; i < sizeof(T); ++i) {
    int bitOffset = (isLittle) ? i : sizeof(T) - i - 1;
    data.append(value >> bitOffset * 8);
  }
  return data;
}

template <typename T>
static void fromData(const QByteArray &data, bool isLittle, T &value) {
  for (int i = 0; i < sizeof(T); ++i) {
    int bitOffset = (isLittle) ? i : sizeof(T) - i - 1;
    value |= (static_cast<quint8>(data[i]) << bitOffset * 8);
  }
}

上面例子代码改为:

  quint32 u32 = 0x12345678;
  //输出小端数据
  QByteArray littleEndian = toData(u32, true);
  qDebug() << "littleEndian:" << littleEndian.toHex();
  quint32 u32x = 0;
  fromData(littleEndian, true, u32x);
  qDebug() << "ori data:" << u32x;

  //输出大端数据
  QByteArray bigEndian = toData(u32, false);
  qDebug() << "bigEndian:" << bigEndian.toHex();
  quint32 u32y = 0;
  fromData(bigEndian, false, u32y);
  qDebug() << "ori data:" << u32y;

编译运行查看打印:

跟前面的一致。

方法二:

思路是使用QDataStream的读写数据,然后借助QDataStream的setByteOrder方法,具体就不多细讲,直接看模板函数:

template <typename T>
QByteArray toData1(T value, bool isLittle) {
  QByteArray data;
  QDataStream stream(&data, QIODevice::WriteOnly);
  if (isLittle)
    stream.setByteOrder(QDataStream::LittleEndian);
  else
    stream.setByteOrder(QDataStream::BigEndian);
  stream << value;
  return data;
}

template <typename T>
void fromData1(const QByteArray &data, bool isLittle, T &value) {
  QDataStream stream(data);
  if (isLittle)
    stream.setByteOrder(QDataStream::LittleEndian);
  else
    stream.setByteOrder(QDataStream::BigEndian);
  stream >> value;
}

将上面例子改为使用这两个方法:

  quint32 u32 = 0x12345678;
  //输出小端数据
  QByteArray littleEndian = toData1(u32, true);
  qDebug() << "littleEndian:" << littleEndian.toHex();
  quint32 u32x = 0;
  fromData1(littleEndian, true, u32x);
  qDebug() << "ori data:" << u32x;

  //输出大端数据
  QByteArray bigEndian = toData1(u32, false);
  qDebug() << "bigEndian:" << bigEndian.toHex();
  quint32 u32y = 0;
  fromData1(bigEndian, false, u32y);
  qDebug() << "ori data:" << u32y;

编译运行查看打印:

与方法一结果一致。

方法三:

借助Qt的QtEndian:

首先qt有判断当前CPU是大端还是小端的宏:

例如:

#if Q_BYTE_ORDER == Q_BIG_ENDIAN
  qDebug() << "current endian is big";
#endif

#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
  qDebug() << "current endian is little";
#endif

编译运行查看打印:

因为我的是x86是小端。

对应Qt也有一些大小端转换的方法:

具体使用有兴趣的可以探究一下,我没有过多研究Qt的这个。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1417168.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【RTP】webrtc 学习3: webrtc对h264的rtp解包

rtp_rtcp\source\video_rtp_depacketizer_h264.cc【RTP】webrtc 学习2: webrtc对h264的rtp打包 中分析了打包过程的代码,这样再来看解析过程的源码就容易多了:本代码主要基于m79,m98类似。这里注明了jitterbuffer 会再次 做 解析stap-a 变为NAL units解析ParseFuaNalu 第一…

Jmeter 分布式测试

Jmeter单机进行压测&#xff0c;受到单台机器的性能影响&#xff0c;Jmeter支持分布式测试&#xff0c;用一个控制节点去控制多个工作节点去模拟更多的用户。 版本信息 内容版本号JDK1.8Jmeter5.6.2 分布式测试原理 jmeter 官网对分布式测试有说明&#xff0c;jmeter分布式…

RabbitMQ 死信交换机的详述➕应用

&#x1f973;&#x1f973;Welcome 的Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于RabbitMQ的相关操作吧 目录 &#x1f973;&#x1f973;Welcome 的Huihuis Code World ! !&#x1f973;&#x1f973; 一.什么是死信交换机 二. 死信队列…

说说你对vue的mixin的理解,有什么应用场景

mixin是什么 Vue中的mixin 局部混入全局混入注意事项: 使用场景源码分析Vue 的几种类型的合并策略 替换型合并型队列性叠加型小结 此文章&#xff0c;来源于印客学院的资料&#xff0c;这里只是分享&#xff0c;便于查漏补缺。 mixin是什么 Mixin 是 面向对象程序设计语言中…

一文理清楚-Docker 容器如何工作

Docker 容器如何工作 集装箱什么是虚拟机&#xff1f;虚拟化如何运作&#xff1f;什么是容器&#xff1f;什么是 Docker&#xff1f;总结 五星上将麦克阿瑟曾经说过&#xff1a;在docker面前&#xff0c;虚拟机就是个弟弟 集装箱 《盒子&#xff1a;集装箱如何让世界变得更小&…

车载电子电器架构 —— 多核处理器刷写策略

车载电子电器架构 —— 多核处理器刷写策略 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消…

内存管理(mmu)/内存分配原理/多级页表

1.为什么要做内存管理&#xff1f; 随着进程对内存需求的扩大&#xff0c;和同时调度的进程增加&#xff0c;内存是比较瓶颈的资源&#xff0c;如何更好的高效的利于存储资源是一个重要问题。 这个内存管理的需求也是慢慢发展而来&#xff0c;早期总线上的master是直接使用物…

C++:STL - string

C&#xff1a;STL - string basic_stringstringstring的常见构造string的输入输出operator<<c_stroperator>>getline string访问及遍历operator[ ] & atfont & back迭代器begin & endrbegin & rend 范围for string的容量操作size & lengthmax_…

用甘特图有效管理多个项目进度

当公司或组织同时承担多个项目时,合理规划各项目的时间节点与资源分配对确保高效完成至关重要。采用甘特图可以直观地展示多个项目的时间进程、关键里程碑以及资源分配情况,便于从宏观层面全面把控各项目的动态。 在线甘特图软件 zz-plan.com 提供了非常强大的时间轴规划功能,支…

CSS 多色正方形上升

<template><view class="loop cubes"><view class="item cubes"></view> <!-- 方块1 --><view class="item cubes"></view> <!-- 方块2 --><view class="item cubes"></vie…

C# 将HTML网页、HTML字符串转换为PDF

将HTML转换为PDF可实现格式保留、可靠打印、文档归档等多种用途&#xff0c;满足不同领域和情境下的需求。本文将通过以下两个示例&#xff0c;演示如何使用第三方库Spire.PDF for .NET和QT插件在C# 中将Html 网页&#xff08;URL&#xff09;或HTML字符串转为PDF文件。 HTML转…

【C语言】深入理解指针(3)数组名与函数传参

目录 &#xff08;一&#xff09;数组名的理解 &#xff08;1&#xff09;数组名是数组首元素的地址 &#xff08;2&#xff09;两个例外 &#xff08;二&#xff09;函数内数组传参 &#xff08;1&#xff09;一维数组传参 &#xff08;2&#xff09;二维数组传参 &…

以太网交换基础VLAN原理与配置

目录 7.以太网交换基础 7.1.以太网协议 7.2.以太网帧介绍 7.3.以太网交换机 7.4.同网段数据通信全过程 8.VLAN原理与配置 8.1.VLAN的基本概念 8.2.VLAN的应用 7.以太网交换基础 7.1.以太网协议 以太网是当今现有局域网(Local Area Network,LAN)采用的最通用的通信协议…

git使用指南——以gitlab为例

注册gitlab 自行注册 新建项目 选择新建一个空白的项目 上传项目 clone项目地址到本地 执行完之后&#xff0c;会在目录下生成如下内容&#xff1a;进入里面&#xff0c;选择.git&#xff0c;要上传的内容&#xff08;资料或代码复制到该目录下&#xff09;&#xff1a;…

界面组件DevExpress .NET MAUI中文教程 - 如何优化手机屏幕空间?

DevExpress拥有.NET开发需要的所有平台控件&#xff0c;包含600多个UI控件、报表平台、DevExpress Dashboard eXpressApp 框架、适用于 Visual Studio的CodeRush等一系列辅助工具。 获取DevExpress v23.2正式版下载 Bottom Sheet是一个组件&#xff0c;它显示固定在屏幕底部的…

LeetCode:376.摆动序列

个人主页&#xff1a;仍有未知等待探索-CSDN博客 专题分栏&#xff1a;算法_仍有未知等待探索的博客-CSDN博客 题目链接&#xff1a;376. 摆动序列 - 力扣&#xff08;LeetCode&#xff09; 一、题目 如果连续数字之间的差严格地在正数和负数之间交替&#xff0c;则数字序列称…

【排序5】基数排序:数字的组织与整理艺术

&#x1f3a1;基数排序 &#x1f38a;1、基本思想&#x1f38a;2、基本步骤&#x1f38a;3、代码示例&#x1f38a;4、特性总结 &#x1f38a;1、基本思想 基数排序&#xff08;Radix Sort&#xff09;是一种非比较排序算法&#xff0c;它根据数字的每一位来对元素进行排序。它…

提示unzip :commad not found; 安装unzip教程

1.当使用unzip解压时 提示&#xff1a; unzip :commad not found; 2.安装命令 sudo yum install unzip 3.输入 y 确认 4.提示&#xff1a;完成 5.输入 unzip 文件名 解压-验证 6.完成

34.基于51单片机的智能停车位计时收费系统设计

一、系统功能介绍&#xff1a; 本设计基于 RFID智能识别和高速的视频图像和存储比较相结合&#xff0c;通过计算机的图像处理和自动识别&#xff0c;对车辆进出停车场的收费、车牌识别和车位诱导等&#xff0c;以实现停车场全方位智能管理。 本设计是以AT89C51 型单片机为主控芯…

Kubernetes (十七) 资源监控

一. 资源监控 二. metrics-server资源下载配置 官网:资源下载&#xff1a;http…