Little Endian & Big Endian

料峭春风吹酒醒發表於2024-04-26

什麼是位元組序

位元組序是指在多位元組資料型別(如整數、浮點數等)的位元組在記憶體中的儲存順序。

主要有兩種位元組序:大端位元組序(Big-endian)和小端位元組序(Little-endian)。

  • Little-Endian 低位元組在記憶體低地址端,高位元組在記憶體高地址端

  • Big-Endian 高位元組在記憶體低地址端,低位元組在記憶體高地址端

address Little Endian Big Endian
0x7000 0x34 0x12
0x7001 0x12 0x34

為什麼會有大小端的分別

大小端問題主要涉及的是非單位元組非字串外的其餘資料的表示和傳遞,如short型、int型等。大端和小端有其各自的優勢。

我們知道計算機正常的記憶體增長方式是從低到高(當然棧不是),取資料方式是從基址根據偏移找到他們的位置,從他們的儲存方式可以看出,大端儲存因為第一個位元組就是高位,從而很容易知道它是正數還是負數,對於一些數值判斷會很迅速。

而小端儲存 第一個位元組是它的低位,符號位在最後一個位元組,這樣在做數值四則運算時從低位每次取出相應位元組運算,最後直到高位,並且最終把符號位重新整理,這樣的運算方式會更高效。

連結:https://www.zhihu.com/question/25311159/answer/33589698

如何判斷主機大小端

//
// Created by zhipeng shu on 2024/4/26.
//

//Little-Endian:低位元組在記憶體低地址端,高位元組在記憶體高地址端,如
//0x7000 is 0x34
//0x7001 is 0x12

//Big-Endian:高位元組在記憶體低地址端,低位元組在記憶體高地址端,如
//0x7000 is 0x12
//0x7001 is 0x34

bool isLittleEndian_1(){
    union Word{
        short value;
        char ch[sizeof(short)];
    };
    Word word = {4660};//0x1234=4660=pow(16,3)+2*pow(16,2)+3*pow(16,1)+4*pow(16,0)
    return word.ch[0]==0x34;
}

bool isLittleEndian_2(){
    short value = 0x1234;//4660
    char ch =  * (char *)&value;//short轉char*,取首元素判斷
    return ch==0x34;
}

網路位元組序

UDP/TCP/IP協議規定:把接收到的第一個位元組當作高位位元組看待。也就是說,傳送端傳送的第一個位元組是需要是高位位元組。

而在傳送端傳送資料時,傳送的第一個位元組是該數值在記憶體中的起始地址處對應的那個位元組,也就是說該數值在記憶體中的起始地址處對應的那個位元組就是要傳送的第一個高位位元組。

所以網路位元組序就是大端位元組序。

有些系統的本機位元組序是小端位元組序, 有些則是大端位元組序, 為了保證傳送順序的一致性, 所以網際協議使用大端位元組序來傳送資料。

如何進行大小端轉換

使用宏定義轉換大小端位元組序


/* Macros for swapping constant values in the preprocessing stage. */
//16位資料轉換大小端
#define __DARWIN_OSSwapConstInt16(x) \
    ((__uint16_t)((((__uint16_t)(x) & 0xff00U) >> 8) | \
	        (((__uint16_t)(x) & 0x00ffU) << 8)))
//32位資料轉換大小端
#define __DARWIN_OSSwapConstInt32(x) \
    ((__uint32_t)((((__uint32_t)(x) & 0xff000000U) >> 24) | \
	        (((__uint32_t)(x) & 0x00ff0000U) >>  8) | \
	        (((__uint32_t)(x) & 0x0000ff00U) <<  8) | \
	        (((__uint32_t)(x) & 0x000000ffU) << 24)))
//64位資料轉換大小端
#define __DARWIN_OSSwapConstInt64(x) \
    ((__uint64_t)((((__uint64_t)(x) & 0xff00000000000000ULL) >> 56) | \
	        (((__uint64_t)(x) & 0x00ff000000000000ULL) >> 40) | \
	        (((__uint64_t)(x) & 0x0000ff0000000000ULL) >> 24) | \
	        (((__uint64_t)(x) & 0x000000ff00000000ULL) >>  8) | \
	        (((__uint64_t)(x) & 0x00000000ff000000ULL) <<  8) | \
	        (((__uint64_t)(x) & 0x0000000000ff0000ULL) << 24) | \
	        (((__uint64_t)(x) & 0x000000000000ff00ULL) << 40) | \
	        (((__uint64_t)(x) & 0x00000000000000ffULL) << 56)))

使用函式轉換大小端


/**
  ******************************************************************************
  * https://blog.csdn.net/weixin_46672094/article/details/122524919
  * @brief   大小端轉換 函式
  * @param   *p     資料塊指標
  * @param   size   位元組數
  * @return  None
  ******************************************************************************
  */
void BigLittleEndianSwap(unsigned char *p, unsigned char size)
{
    unsigned char i;
    unsigned char tmp;
    unsigned char num = size/2;
    size--;
    for(i=0; i<num; i++)
    {
        tmp = p[i];
        p[i] = p[size-i];
        p[size-i] = tmp;
    }
}

本地位元組序和網路位元組序的轉換

htons() //host to network short
htonl() //host to network long
ntohs() //network to host short
ntohl() //network to host long

在unix系統中,可以使用man htons指令檢視手冊

使用示例

short value = 0x1234;
std::cout<<std::dec<<"value="<<value<<",hex="<<std::hex<<value<<std::endl;
//value=4660,bits=0001001000110100,hex=1234

value = htons(value); //將value從主機端轉換為網路位元組序

std::cout<<std::dec<<"value="<<value<<",hex="<<std::hex<<value<<std::endl;
//value=4660,bits=0001001000110100,hex=1234

相關文章