仔细想想,写一些简单的记录应该不影响工作
内存存放方式
计算机内存中,一字节8bit,每比特只能存放0或者1。其中每个字节的第一个bit所拥有的内存地址代表了其所在的字节内容。
例如:
char *p = "aBcDe";
printf("mem:%x , value:%s\n",&p,p);
//输出结果 mem:ffffcc20 , value:aBcDe
printf("mem:%x , value:%s\n",&p+1,(p+1));
//输出结果 mem:ffffcc28 , value:BcDe
每个char类型占据一个字节即八个bit位。
第一次输出时,起始地址为ffffcc20,代表了ffffcc20至ffffcc27该字节整体存放内容,内容为a。
第二次输出时,起始地址为ffffcc28,代表了ffffcc28至ffffcc2F该字节整体存放内容,内容为B。
由此可得出,在将内存存放内容输出时,是按照字节顺序从小至大依次输出。
主机字节序
字节序故名思意,字节的顺序。
字节顺序按照存储方式分为大端存储和小端存储。
我们将IP地址作为连续内容存放至内存中,例如:192.168.123.234,将其传换成二进制形式存放在计算机中,并打印其信息
#include<stdio.h>
#include<string.h>
#include "config.h"
#include "tranIP.h"
int main()
{
char ip[15] = "192.168.123.234";
in_addr_t ip_bin;
ip_bin = inet_addr(ip);
printf("%2x\n",ip_bin);
/*
* printf信息为:ea 7b a8 c0
* 二进制:1110 1010 0111 1011 1010 1000 1100 0000
* 十进制:234 123 168 192
*
*
* */
return 0;
}
可以看到字节内部无变化,字节之间变为倒序。由此推出其存放方式如下:
内存地址 | IP地址 |
---|---|
0F0F 0F0F 0F0F 0001 | 234 |
0F0F 0F0F 0F0F 0008 | 123 |
0F0F 0F0F 0F0F 0010 | 168 |
0F0F 0F0F 0F0F 0018 | 192 |
其中对于我输入的192168123234来说,从左至右为高位至低位,其中192占用一个字节,为高位字节(高和低为相对)。简单来说,数据的高字节保存在了内存的高地址中(如同古代人的读书方式),可判断出我的电脑为小端存放。
也可用C中的共用体来判断存放方式:
union w {
int a;
char b;
} c;
c.a = 1;
if (c.b == 1)
printf ("little endian\n");
else
printf ("big endian");
网络字节序
主机字节序根据型号而不同,为了统一在网络中传输时可能出现的问题,将网络字节序规定为大端存放方式,即高位数据存放至低地址中(如同现代人的读书方式)。
在socket通信中,我们需要将网络地址以网络字节序绑定到本地的缓冲区中。
转换函数
htons();htonl();ntohs();ntohl();
这里使用htonl()将主机字节序变为网络字节序的long类型,再使用htonl()将网络字节序变为主机字节序的long类型
#include<stdio.h>
#include<string.h>
#include "config.h"
#include "tranIP.h"
int main()
{
char ip[15] = "192.168.123.234";
in_addr_t ip_bin;
ip_bin = inet_addr(ip);
//该函数将点分十进制转换为网络传输格式(二进制形式)存放
//但其是主机字节序而不是网络字节序
printf("%2x\n",ip_bin);
/*
* ea 7b a8 c0
* 14 10 7 11 10 8 12 0
* 二进制:1110 1010 0111 1011 1010 1000 1100 0000
* 十进制:234 123 168 192
*
*
* */
u_int32_t ip_nb;
ip_nb = htonl(ip_bin);
//该函数将主机字节序转换为网络字节序形式存储在内存中
printf("%2x\n",ip_nb);
/*
* 输出为 c0 a8 7b ea
*
* */
u_int32_t ip_hb;
ip_hb = ntohl(ip_nb);
//该函数将网络字节序转换为主机字节序存储在内存中
printf("%2x\n",ip_hb);
/*
* 输出为 ea 7b a8 c0
* */
return 0;
}
当处理器为intel的小端存放方式,并且你需要输出某段内存地址的内容时。需要考虑这段内容是以什么样的方式存储进去的,即:
当你亲自键入并存放时,其存储方式为小端存储并且读取方式也为小端存储,那么在读取时毫无差别,如你将0x12345678对应的二进制按内存低位至高位存放为0x78563412,并亲自读取时,系统倒叙读取,这样不会产生倒序情况仍为0x12345678.
当你使用库函数存放0x12345678时,存储的数据可能会按照提供的方法存储为0x12345678而并非小端方式,因此在读取时按照小端方式读取会出现0x78563412逆序的情况。
在编写代码时应注意,在使用网络字节序转换函数后,转换后的输出可能会出现倒叙的情况,例如在inet_ntoa()之后,发现输出的IP地址为逆序,如果该电脑为小端存放的话,这其实是没有问题的,因为实际的二进制在内存中已经按照网络字节序(大端存储)的方式留存,而在经过inet_ntoa()转换后,该二进制转换为点分十进制后在内存的存储方式依然为大端,在输出时变为小端输出。即出现了倒叙情况。
如果要输出正确的点分十进制可以先转换为主机字节序后再调用该方法转换成点分十进制。