大端 & 小端
大小端之定义
计算机系统是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。
在几乎所有的机器上,对于跨越多字节的程序对象,往往都是被连续存储的,对象的地址为所使用的字节中最小的地址。
在多字节的程序对象中,对不同的字节有两种排列方式:大端和小端。(大小端之争就如打鸡蛋从大头打还是从小头打,没有实际意义。大小端的起源也是这个,狗头)
大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中。(高地址存低位)
小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。(高地址存高位,对应到内存中读是个反的)
假如32位宽(uint32_t)的数据0x12345678,从地址0x08004000开始存放:
地址 | 小端存放内容 | 大端存放内容 |
---|---|---|
0x08004003(高地址) | 0x12 | 0x78 |
0x08004002 | 0x34 | 0x56 |
0x08004001 | 0x56 | 0x34 |
0x08004000(低地址) | 0x78 | 0x12 |
大小端之实例
判断本机大小端的方法:
#include <stdio.h>
void byteorder()
{
union
{
short value;
char union_bytes[ sizeof( short ) ];
} test;
test.value = 0x0102;
if ( ( test.union_bytes[ 0 ] == 1 ) && ( test.union_bytes[ 1 ] == 2 ) )
{
printf( "big endian\n" );
}
else if ( ( test.union_bytes[ 0 ] == 2 ) && ( test.union_bytes[ 1 ] == 1 ) )
{
printf( "little endian\n" );
}
else
{
printf( "unknown...\n" );
}
}
int main() {
byteorder();
}
大端模式:Sun、PowerPC、IBM
小端模式:x86、DEC
ARM既可以工作在大端模式,也可以工作在小端模式,取决于特定的操作系统。该芯片常用的操作系统Android和iOS采用小端模式。
大小端与网络编程
为了解决大小端机子之间通信时存在的问题,在网络编程中也有个字节序的概念:
网络字节序:大端模式(高地址存低位)
主机字节序:小端模式(高地址存高位)
发送端总是把要发送的数据转化成大端字节序数据后再发送,而接收端知道传送的数据是大端字节序,可以根据自身的字节序来决定是否转换。
字节序转换函数:
#include <netinet/in.h>
//h 指代 host(主机),n 指代 net(网络),l 是 long,s 是 short
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);
IP地址转换函数
IP地址实际上是一个uint32_t的整数,将每一个字节读成一个十进制数,并加点区分便是点分十进制(127.0.0.1),这是我们通常所见的IP地址表示方法。但是点分十进制是一个字符串,对于计算机来说,它是不好识别的。所以需要涉及到点分十进制和uint32_t的转换。
#include <arpa/inet.h>
//点分十进制转换成二进制
in_addr_t inet_addr(const char* strptr); //失败返回INADDDR_NONE
int inet_aton(const char* cp, struct in_addr* inp); //这个函数将转换结果存储结构体中,而不是返回。成功时返回1,失败返回0
//二进制转换为点分十进制
char* inet_ntoa(struct in_addr in); //该函数内存用一个静态变量存储转化结构,且不可重入
当然,下面这对更新函数也能完成和前面3个函数同样的功能,并同时适用IPv4和IPv6。
#include <arpa/inet.h>
int inet_pton(int af, const char* src, void* dst); //成功返回1,失败返回0并设置errno
const char* inet_ntop(int af, const void* src, char* dst, socklen_t cnt); //成功返回指向目标存储单元的地址,失败则返回NULL并设置errno
reference
[1] 深入理解计算机系统
[2] Linux高性能服务器编程