记录本篇的缘由是在一次面试题中,面试官问起大小端的问题,没有答出来。这里做一份笔记。
主要内容参考这篇博客。
定义
大端模式(Big-endian):高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
小端模式(Little-endian):低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
面试的时候记住一个词就够了,“小高高”,即:小端模式–高字节数据–高地址位置。
字节顺序高低
以一个4字节的整型数值 int a = 0x12345678
为例,该数的十六进制,十进制及二进制分别为:
HEX 12345678
DEC 305,419,896
BIN 0001 0010, 0011 0100, 0101 0110, 0111 1000
那么二进制及十六进制的数位对应关系为:
0001 0010 | 0011 0100| 0101 0110 | 0111 1000 (二进制)
0x12 | 0x34 | 0x56 | 0x78 (十六进制)
高字节 -----------------> 低字节
地址位置高低
同样以这个4字节的整型数值 a = 0x12345678
为例,目前不清楚该数值在机器上的存储顺序,但是如果对其分配内存空间,则四个字节的顺序假如是一段0x0001
到0x0004
的内存段,那么内存的增长顺序即为低地址到高地址。
0x0001 | 0x0002 | 0x0003 | 0x0004
低地址 ------------------> 高地址
代码验证
采用两种方法检查本机器上的大小端模式。
#include <iostream> int main() { #if 1 union { int ia; char ca[4]; } un; un.ca[0] = 0x12; un.ca[1] = 0x34; un.ca[2] = 0x56; un.ca[3] = 0x78; printf("0x%x,%d\n",un.ia,sizeof(int)); if(un.ia == 0x12345678) printf("big endien\n"); else if(un.ia == 0x78563412) printf("little endien\n"); #else int ia = 0x12345678; char *ca = (char*)(&ia); printf("0x%x,%d\n",*ca,sizeof(int)); if(*ca == 0x12) printf("big endien\n"); else if(*ca == 0x78) printf("little endien\n"); #endif return 0; }
x86机器上是小端模式。PowerPc机器上是大端模式。网络字节传输采用大端模式。
大小端转换
#include<stdio.h> typedef unsigned int uint_32 ; typedef unsigned short uint_16 ; //16位 #define BSWAP_16(x) \ (uint_16)((((uint_16)(x) & 0x00ff) << 8) | \ (((uint_16)(x) & 0xff00) >> 8) \) //32位 #define BSWAP_32(x) \ (uint_32)((((uint_32)(x) & 0xff000000) >> 24) | \ (((uint_32)(x) & 0x00ff0000) >> 8) | \ (((uint_32)(x) & 0x0000ff00) << 8) | \ (((uint_32)(x) & 0x000000ff) << 24) \) //无符号整型16位 uint_16 bswap_16(uint_16 x) { return (((uint_16)(x) & 0x00ff) << 8) | \ (((uint_16)(x) & 0xff00) >> 8) ; } //无符号整型32位 uint_32 bswap_32(uint_32 x) { return (((uint_32)(x) & 0xff000000) >> 24) | \ (((uint_32)(x) & 0x00ff0000) >> 8) | \ (((uint_32)(x) & 0x0000ff00) << 8) | \ (((uint_32)(x) & 0x000000ff) << 24) ; } int main(int argc,char *argv[]) { printf("------------带参宏-------------\n"); printf("%#x\n",BSWAP_16(0x1234)) ; printf("%#x\n",BSWAP_32(0x12345678)); printf("------------函数调用-----------\n"); printf("%#x\n",bswap_16(0x1234)) ; printf("%#x\n",bswap_32(0x12345678)); return 0 ; } 输出结果: ------------带参宏------------- 0x3412 0x78563412 ------------函数调用----------- 0x3412 0x78563412
大小端转换可以采用以上的位操作方式,也可以采用使用 htonl, htons,ntohl,ntohs 等函数实现。读者有需要可自行参考本篇开头放置的博客链接,进行深入了解。