[C/C++]_[初級]_[大端序和小端序理解]
場景
在進行Socket通訊時, 因為網路位元組序是 Big-Endian模式(標準), 而大部分Windows系統都是 Little Endian模式, 所以在傳輸數值型別的資料時, 需要把 Little Endian的記憶體資料轉換為 Big-Endian再發送.
如果客戶端和服務端都是你自己實現的程式碼, 可以不需要轉換, 因為你自己知道位元組序.
因為 Big-Endian的系統很少見,其實大部分情況下需要進行轉換都是和網路相關的.
說明
不同計算機基礎結構有時儲存使用不同的位元組順序的資料, 我們所使用的 Intel,x86架構都是 Little-Endian模式儲存資料.
Big-Endian: 就是低地址儲存資料高位. Little-Endian: 低地址儲存資料低位.
Windows系統為我們提供了兩種API來處理大小端位元組序處理,還是比較方便的. 如果是處理double型別的資料, 需要自己轉換. 第一種是WinSock2提供的轉換函式, 比如
ntohl
. 另一種是標準庫擴充套件, 比如_byteswap_ulong
. 第一種明確的是在網路位元組序Big-Endian和宿主系統之間轉換, 第二種是進行位元組位反轉, 沒有明確輸入輸出是 Little-Endian還是 Big-Endian, 使用時要注意.當然, 如果理解了大小端其實位元組反轉的原理之後也可以自己寫出來,其實真沒那個必要.
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