计算机使用8位字节块作为最小寻址和存储单位,一个字节在二进制里值域为00000000~11111111,十进制就是0~255,而这种表示方法要么太长了,要么转换太麻烦,所以就用十六进制来描述,值域为00~FF,把A的二进制记住就行了,后面就好推算了,当然大脑比较灵活的人啥都不用记,直接心算一遍好了~!
十六进制 十进制 二进制 0 0 0000 1 1 0001 2 2 0010 3 3 0011 4 4 0100 5 5 0101 6 6 0110 7 7 0111 8 8 1000 9 9 1001 A 10 1010 B 11 1011 C 12 1100 D 13 1101 E 14 1110 F 15 1111
十六进制转换二进制比较容易,而十进制和十六进制的转换则需要使用计算;将一个十进制数字a转换成十六进制,可以不停用16除a,得到商b和余数c,也就是a = b * 16 + c,将c转换成16进制作为整个十六进制数字的最低位;然后不停对商b反复进行这个过程即可
比如,十进制13567转换成十六进制:
13567 = 847 * 16 + 15 F 847 = 52 * 16 + 15 F 52 = 3 * 16 + 4 4 3 = 0 * 16 + 3 3
所以十六进制表示为0x34FF,如果是手工,更直观的是习惯的除法算式直接列下来
十六进制转换成十进制就比较容易了,乘以16的位数减一次幂加起来就完了
假如有太大的数字需要转换,直接让计算机才操作,或者一个小程序printf
#!/usr/bin/perl -w while(<STDIN>){ chomp; printf("%d\t = 0x%x\n", $_, $_); } lihui@2015 $ perl lihui.pl 100 100 = 0x64 134314 134314 = 0x20caa 9999 9999 = 0x270f
寻址和字节序
通常一个变量会考虑它的地址是什么,在存储器里如何排列这些字节;一般多字节变量会存储为连续的字节序列,地址是所使用的字节当中最小的地址;比如一个int的变量x的地址为0x100,那么表达式&x的值为0x100,那么x的四个字节将被存储在存储器的0x100,0x101,0x102,0x103这四个位置当中
对于一个w位的整数,位表示为 [ x(w-1),x(w-2),…,x(1),x(0)],括号里面的为下标,这里从左到右,有效位从高到低,,如果w是8的倍数,这些位就正好能够分组称字节,其中最高有效字节为[ x(w-1),x(w-2),…,x(w-7),x(w-8)],最低有效字节为[ x(7),x(6),…,x(1),x(0)]
小端和大端
在存储器中按照从最低有效字节到最高有效字节的顺序存储的方式为小端法,little endian
在存储器中按照从最高有效字节到最低有效字节的顺序存储的方式为大端法,big endian
对于上面int变量x,地址为0x100,假如十六进制值为0x01234567,地址范围0x100~0x103
大端法 0x100 0x101 0x102 0x103 ...... 01 23 45 67 ...... 小端法 0x100 0x101 0x102 0x103 ...... 67 45 23 01 ......
在编写程序时,机器所使用的字节序是完全不可见的,有的时候字节序会出问题
1:在不同字节序类型的机器之间通过网络传输二进制数据的时候,当小端机器产生的数据被发送到大端的机器或者反向,接受程序里的字节就会反序;为了避免问题出现,网络编程就必须遵守已建立的关于字节序规则,确保发送机器将它的内部表示转换成网络标准,而接收机器就将网络标准转换成它的内部规则
2:在阅读表示整数数据的字节序列的时候字节序也很重要,下面从网上copy了这行代码
80483bd: 01 05 64 94 04 08 add %eax, 0x8049464
如何生成就不管了,总是汇编那些玩意;这行描述了十六进制字节串01 05 64 94 04 08是一条字节级表示,这条指令把一个字长的数据加到存储在主存地址0x8049464的值上;如果取出这个序列的后4个字节:64 94 04 08,按照相反顺序写出来,就是08 04 94 64,去掉开始的0就变成了值0x8049464,也就是右边的值;在看此类小端机器生成的程序,要将字节按照相反顺序显示;通常在书写字节序列的时候自然方式是最低字节放左边,最高字节放右边,而书写数字的时候自然方式是最高有效位放左边,最低有效位放右边
3:下面使用了强制转换类型来访问和打印不同变量字节表示
#include <stdio.h> #include <malloc.h> typedef unsigned char *byte_pointer; void show_bytes(byte_pointer start, int len){ int i; for (i = 0; i < len; i++) printf(" %.2x", start[i]); printf("\n"); } void show_int(int x){ show_bytes((byte_pointer)&x, sizeof(int)); } void show_float(float x){ show_bytes((byte_pointer)&x, sizeof(float)); } void show_pointer(void *x){ show_bytes((byte_pointer)&x, sizeof(void *)); } int main(){ int a = 3510593; float b = 3510593.0; void *c = malloc(sizeof(void *)); show_int(a); show_float(b); show_pointer(c); return 0; } lihui@2015 /cygdrive/d/work $ ./a.exe 41 91 35 00 04 45 56 4a f0 03 01 00 06 00 00 00
通过show_int,show_float和show_pointer来表示如何通过show_bytes输出类型为int,float,void *的变量的字节表示;仅仅穿进去了一个指向参数x的指针&x,而且这个指针被强行转换成了unsigned char *;这个强制转换就告诉编译器,程序应该把这个指针看成是指向一个字节序列,而不是指向一个原始数据类型;这个指针被看作是最低字节地址
因为inter是小端,那么3510593的十六进制为0x00359141,而浮点型为0x4a564505,转换成二进制
00359141 0000 0000 0011 0101 1001 0001 0100 0001 4a564504 0100 1010 0101 0110 0100 0101 0000 0100 0100 1010 0101 0110 0100 0101 0000 0100
整型和浮点型右移两位的结果中间有21位一致,整型的二进制里除了最高有效位1之外,所有位都可以镶嵌在浮点数中