Qt应用软件【数据篇】大小端数据转换

文章目录

  • 大小端数据介绍
  • 大小端数据在内存中的样子
  • C++大小端数据转换
  • QtAPI大小端转换

大小端数据介绍

大端(Big Endian)和小端(Little Endian)是一种描述计算机存储多字节数据的方式。

想象一下,你有一串数字,例如1234,这个数字需要用两个字节(或更多)来存储。那么问题是,这个数字的哪个部分先存储在内存的起始位置(低地址),哪个部分先存储在内存的结束位置(高地址)

  • 大端:在大端字节序中,像1234这样的数字的高位数字(例如12)会存储在内存的低地址处,而低位数字(例如34)会存储在内存的高地址处。这就好比你在阅读数字时,先读高位再读低位,就像阅读英文文字一样,从左到右。
  • 小端:在小端字节序中,相同的数字1234的低位数字(例如34)会存储在内存的低地址处,而高位数字(例如12)会存储在内存的高地址处。这就好比你在阅读数字时,先读低位再读高位,就像阅读阿拉伯数字一样,从右到左。

不同的计算机架构使用不同的字节序,没有统一的标准。通常,x86架构(包括大多数个人电脑和服务器)使用小端字节序,而一些其他架构如PowerPC和SPARC使用大端字节序。因此,当你在处理二进制数据或者与其他系统进行数据交互时,需要注意字节序的差异,以确保数据被正确解释和处理。这也是为什么在网络通信中会有网络字节序(通常是大端)和主机字节序(取决于计算机架构)之间的转换

大小端数据在内存中的样子

大端(Big Endian)和小端(Little Endian)是两种不同的内存存储格式,它们定义了多字节数据(如整数、浮点数等)在内存中的排列方式。理解这两种格式对于编程和系统设计非常重要,特别是在涉及到网络通信和跨平台数据交换时。

大端模式(Big Endian)

在大端模式下,数据的"大端"(即最高有效字节)存储在内存的低地址端,而数据的"小端"(即最低有效字节)存储在内存的高地址端。这意味着,比如一个32位的整数0x12345678,在内存中的存储顺序(从低地址到高地址)将是:

0x12 0x34 0x56 0x78

小端模式(Little Endian)

相反,在小端模式下,数据的"小端"(即最低有效字节)存储在内存的低地址端,而数据的"大端"(即最高有效字节)存储在内存的高地址端。同样的32位整数0x12345678,在内存中的存储顺序将是:

0x78 0x56 0x34 0x12
Little Endian Memory Layout
Big Endian Memory Layout
0x00: 0x78
0x01: 0x56
0x02: 0x34
0x03: 0x12
0x00: 0x12
0x01: 0x34
0x02: 0x56
0x03: 0x78

在这个图表中,我们展示了一个32位整数(0x12345678)在大端和小端模式下的内存布局。每个方框代表一个字节的内存位置,箭头表示地址的增加方向。从图表中可以清晰地看出,大端模式将最高有效字节放在最低的内存地址处,而小端模式则相反,将最低有效字节放在最低的内存地址处。

通过这样的图表,可以直观地理解大端和小端模式的不同,以及它们在内存中是如何存储数据的。

C++大小端数据转换

  • memcpy大小端转换
// 大端转小端
uint16_t bigEndianValue = 0x1234;
uint16_t littleEndianValue;
memcpy(&littleEndianValue, &bigEndianValue, sizeof(uint16_t));


// 小端转大端
uint16_t littleEndianValue = 0x3412;
uint16_t bigEndianValue;
memcpy(&bigEndianValue, &littleEndianValue, sizeof(uint16_t));
  • 宏函数大小端转换
uint16_t bigEndianValue = 0x1234;
uint16_t littleEndianValue = BIG_ENDIAN_TO_LITTLE_ENDIAN_16(bigEndianValue);

uint32_t anotherBigEndianValue = 0x12345678;
uint32_t anotherLittleEndianValue = BIG_ENDIAN_TO_LITTLE_ENDIAN_32(anotherBigEndianValue);

  • boost库
#include 

uint16_t bigEndianValue = 0x1234;
uint16_t littleEndianValue = boost::endian::endian_reverse(bigEndianValue);

  • C++20判断大小端
#include 
#include 

int main() {
    if (std::endian::native == std::endian::big) {
        std::cout << "This system uses big endian." << std::endl;
    } else if (std::endian::native == std::endian::little) {
        std::cout << "This system uses little endian." << std::endl;
    } else {
        std::cout << "Unknown endian." << std::endl;
    }

    return 0;
}
  • 结构体的大小端转换
#include 
#include 
#include 

//1字节对齐
#pragma pack(push, 1)
struct MyStruct {
    uint16_t a;
    uint32_t b;
    uint16_t c;
};
#pragma pack(pop)

MyStruct bigToLittleEndian(const MyStruct& bigEndianValue) {
    MyStruct littleEndianValue;
    std::memcpy(&littleEndianValue, &bigEndianValue, sizeof(MyStruct));
    return littleEndianValue;
}

MyStruct littleToBigEndian(const MyStruct& littleEndianValue) {
    return bigToLittleEndian(littleEndianValue);
}

int main() {
    MyStruct bigEndianValue = {0x1234, 0x56789ABC, 0x5678};
    MyStruct littleEndianValue = bigToLittleEndian(bigEndianValue);

    std::cout << "Big Endian Value: " << std::hex << bigEndianValue.a << " " << bigEndianValue.b << " " << bigEndianValue.c << std::endl;
    std::cout << "Little Endian Value: " << std::hex << littleEndianValue.a << " " << littleEndianValue.b << " " << littleEndianValue.c << std::endl;

    return 0;
}

QtAPI大小端转换

  • QByteArray
#include 
#include 

// 大端转小端
QByteArray bigToLittleEndian(const QByteArray &bigEndianData) {
    QByteArray littleEndianData(bigEndianData);
    for (int i = 0; i < littleEndianData.size(); i++) {
        littleEndianData[i] = bigEndianData.at(littleEndianData.size() - 1 - i);
    }
    return littleEndianData;
}

// 小端转大端
QByteArray littleToBigEndian(const QByteArray &littleEndianData) {
    QByteArray bigEndianData(littleEndianData);
    for (int i = 0; i < bigEndianData.size(); i++) {
        bigEndianData[i] = littleEndianData.at(bigEndianData.size() - 1 - i);
    }
    return bigEndianData;
}

int main() {
    uint16_t bigEndianValue = 0x1234;
    QByteArray bigEndianData(reinterpret_cast(&bigEndianValue), sizeof(uint16_t));

    QByteArray littleEndianData = bigToLittleEndian(bigEndianData);
    QByteArray backToBigEndian = littleToBigEndian(littleEndianData);

    // 输出结果
    qDebug() << "Big Endian Data:" << bigEndianData.toHex();
    qDebug() << "Little Endian Data:" << littleEndianData.toHex();
    qDebug() << "Back to Big Endian Data:" << backToBigEndian.toHex();

    return 0;
}

  • QDataStream
QDataStream stream(&byteArray, QIODevice::ReadWrite);
stream.setByteOrder(QDataStream::LittleEndian); // 设置为小端
quint16 value;
stream >> value; // 从小端数据中读取16位无符号整数

  • QByteArray
quint16 bigEndianValue = 0x1234;
QByteArray byteArray = QByteArray(reinterpret_cast(&bigEndianValue), sizeof(quint16));
quint16 littleEndianValue = qFromBigEndian(byteArray.constData());

  • 判断大小端
if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
    // 当前系统是小端
} else {
    // 当前系统是大端
}
  • qFromLittleEndian、qToLittleEndian
quint16 littleEndianValue = 0x1234;
quint16 nativeValue = qFromLittleEndian(littleEndianValue); // 转换为本机字节顺序


quint16 nativeValue = 0x1234;
quint16 littleEndianValue = qToLittleEndian(nativeValue); // 转换为小端表示

你可能感兴趣的:(Qt上位机,qt,C++,C)