1. 程式人生 > >[C/C++]_[初級]_[大端序和小端序理解]

[C/C++]_[初級]_[大端序和小端序理解]

場景

  1. 在進行Socket通訊時, 因為網路位元組序是 Big-Endian模式(標準), 而大部分Windows系統都是 Little Endian模式, 所以在傳輸數值型別的資料時, 需要把 Little Endian的記憶體資料轉換為 Big-Endian再發送.

  2. 如果客戶端和服務端都是你自己實現的程式碼, 可以不需要轉換, 因為你自己知道位元組序.

  3. 因為 Big-Endian的系統很少見,其實大部分情況下需要進行轉換都是和網路相關的.

說明

  1. 不同計算機基礎結構有時儲存使用不同的位元組順序的資料, 我們所使用的 Intel,x86架構都是 Little-Endian模式儲存資料.

  2. Big-Endian: 就是低地址儲存資料高位. Little-Endian: 低地址儲存資料低位.

  3. Windows系統為我們提供了兩種API來處理大小端位元組序處理,還是比較方便的. 如果是處理double型別的資料, 需要自己轉換. 第一種是WinSock2提供的轉換函式, 比如 ntohl. 另一種是標準庫擴充套件, 比如 _byteswap_ulong. 第一種明確的是在網路位元組序Big-Endian和宿主系統之間轉換, 第二種是進行位元組位反轉, 沒有明確輸入輸出是 Little-Endian還是 Big-Endian, 使用時要注意.

  4. 當然, 如果理解了大小端其實位元組反轉的原理之後也可以自己寫出來,其實真沒那個必要.

Big-Endian  The most significant byte is on the left end of a word.
Little-Endian   The most significant byte is on the right end of a word

ntohs   Convert a 16-bit quantity from network byte order to host byte order (big-Endian to little-Endian).
ntohl   Convert a 32-bit quantity from network byte
order to host byte order (big-Endian to little-Endian). Htons Convert a 16-bit quantity from host byte order to network byte order (little-Endian to big-Endian). Htonl Convert a 32-bit quantity from host byte order to network byte order (little-Endian to big-Endian).

例子

下邊的程式碼演示了這兩種API的使用方法:

// test-big-endian.cpp : 定義控制檯應用程式的入口點。
//

#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
#include <assert.h>
#include <WinSock2.h>
#include <stdint.h>

struct Message
{
    long m_lMagicNumber;
    short m_nCommand;
    short m_nParam1;
    long m_lParam2;
};

// 判斷CPU的儲存是大端序還是小端序.
bool IsLittleEndian()  
{  
    union w  
    {  
        int a;  
        char b;  
    }w1;  

    w1.a = 1;  
    return (w1.b == 1);  
} 

int32_t ToBigEndian(int32_t value)
{
    if(IsLittleEndian())
        return _byteswap_ulong(value);
    else
        return value;
}

uint32_t ToLittleEndian(uint32_t value)
{
    if(IsLittleEndian())
        return value;
    else
        return _byteswap_ulong(value);
}

double ToLittleEndian(double value)
{
    if(IsLittleEndian()){
        return value;
    }else{
        auto temp = _byteswap_uint64(value);
        return *(double*)&temp;
    }
}

uint8_t ToLittleEndian(uint8_t value)
{
    if(IsLittleEndian())
        return value;
    else
        return _byteswap_ushort(value);
}

int _tmain(int argc, _TCHAR* argv[])
{
    if(!IsLittleEndian())
        return -1;

    std::cout << "Little Endian" << std::endl;

    long value = -1;
    auto value_sock_bigendian1 = htonl(value);
    auto value_byteswap_bigendial1 = ToBigEndian(value);
    assert(value_sock_bigendian1 == value_byteswap_bigendial1);
    std::cout << "1->Little Endian: " << value_sock_bigendian1 << std::endl;

    auto value_nl_1 = ntohl(value_sock_bigendian1);
    auto value_swap_1 = _byteswap_ulong((uint32_t)value_byteswap_bigendial1);

    assert(value_nl_1 == value_swap_1);
    std::cout << "to host byte : " << value_nl_1 << std::endl;

    return 0;
}

輸出:

Little Endian
1->Little Endian: 4294967295
to host byte : 4294967295

參考

Windows Sockets Byte Ordering
Windows 套接字 位元組排序
大端模式和小端模式
byteswap_uint64
how-do-i-convert-between-big-endian-and-little-endian-values-in-c
判斷大小端序Little Endian Order