大端模式和小端模式
对于数字0x12345678在内存中的表示形式。
-
大端模式(Big-Endian):就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。 低地址-------------->高地址
0x12 | 0x34 | 0x56 | 0x78 -
小端模式(Little-Endian):就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 低地址-------------->高地址
0x78 | 0x56 | 0x34 | 0x12
为什么会有大小端模式之分?
在计算机系统中,每一个地址单元都对应一个字节,一个字节为8bit,但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此才有了大端模式和小端模式。
常见的字节序
一般操作系统都是小端,而通信协议都是大端。
CPU字节序:
Big Endian : PowerPC、IBM、Sun
Little Endian : x86、DEC
ARM既可以工作在大端模式,也可以工作在小端模式。
文件字节序:
GIF – Little Endian
JPEG – Big Endian
htons()、htonl()、ntohl()、ntohs()函数
从软件的角度上看,不同端模式的处理器进行数据传递时必须要考虑端模式的不同。如进行网络数据传递时,必须要考虑端模式的转换。以下几个函数用于大小端字节序的转换:
#define ntohs(n) //16位数据类型网络字节顺序到主机字节顺序的转换
#define htons(n) //16位数据类型主机字节顺序到网络字节顺序的转换
#define ntohl(n) //32位数据类型网络字节顺序到主机字节顺序的转换
#define htonl(n) //32位数据类型主机字节顺序到网络字节顺序的转换
其中互联网使用的网络字节顺序采用大端模式进行编址,而主机字节顺序根据处理器的不同而不同,如PowerPC处理器使用大端模式,而Pentuim处理器使用小端模式。
大端模式处理器的字节序到网络字节序不需要转换,而小端模式处理器的字节序到网络字节必须要进行转换。 不过为了移植性,在转换成网络字节序时,最好都使用htons()或htonl()函数。
inet_pton()、inet_ntop()函数
这两个函数对于IPv4和IPv6地址都适用,函数名中p表示表达(presentation),n代表数值(numeric)。
函数原型如下:
#include <arpe/inet.h>
/*
将点分十进制的ip地址转化为用于网络传输的数值格式
返回值:若成功则为1,若输入不是有效的表达式则为0,若出错则为-1
*/
int inet_pton(int family, const char* strptr, void* addrptr);
/*
将数值格式转化为点分十进制的ip地址格式
返回值:若成功则为指向结构的指针,若出错则为NULL
*/
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
- 两个函数的family参数既可以是AF_INET,也可以是AF_INET6。如果,传入了不支持的地址族,那么两个函数会返回错误,并将errno置为EAFNOSUPPORT。
- 第一个函数尝试转换由strptr指针所指向的字符串,并通过addrptr指针存放二进制结果,若成功则返回值为1,否则如果对于所指定的family而言输入字符串不是有效的表达式格式,那么返回值为0.
- inet_ntop进行相反的转换,从数值格式(addrptr)转换到表达式(strptr)。inet_ntop函数的strptr参数不可以是一个空指针。调用者必须为目标存储单元分配内存并指定其大小,调用成功时,这个指针就是该函数的返回值。len参数是目标存储单元的大小,以免该函数溢出其调用者的缓冲区。如果len太小,不足以容纳表达式结果,那么返回一个空指针,并置errno为ENOSPC。
eg:
const char* src = "192.168.0.1";
struct sockaddr_in foo;
inet_pton(AF_INET, src, &foo.sin_addr);
char str[INET_ADDRSTRLEN];
char *ptr = inet_ntop(AF_INET, &foo.sin_addr, str, sizeof(str));