有些部分写得很简洁,不理解的话你就去对应部分看书就懂了,还有的部分对不齐,我这边自己写的时候是对齐的,他们后台生成就不齐了,暂时不花很多的时间排版。 如果你们大学是学这本书的话,那就变相的相当于给你们画重点了,
编码的格式:
无符号编码:基于传统的二进制编码大于0的数
补码:可表示负数
浮点数:编码方式很特殊,下面细说。
特性一:
由于表示的精度有限,其是不可结合的。
给了一个例子:
(3.14+le20)-le20 = 0
3.14+(le20-le20) = 3.14
2.1信息存储
提了一下指针的重要性,指针分为类型和值。每一个常规类型的数据类型都有其对应的内存大小
也就是int 4字节,char 1字节。下面章节会介绍....
编译器和运行时的系统是如何将存储器空间分为不同的存储单元来存放不同的程序对象,
数据指令,控制信息等。
其实理解了编码格式或者说上面一章的数据最终在机器上面呈现的形式,那么你就理解了强制转化,
强制转化会出错的本质是数据的大小没有搞清楚,导致越界或者数据被截断。
也就是强转的时候,你要获取数据存储所在的完整的地址不能减少,也不能越界。
当然也可以利用这一点对于char * 字符类数据的地址偏移来获取协议好的数据。
2.1.1
一个字节可以用两个十六进制数表示,八个二进制数。
十六进制表示法:
二进制 0000 ~ 1000 1001 ~1111
十六进制 0 ~ 8 9 ABCDEF
进制的转化 二进制和十六进制很好转化
小的二进制又相对好转为为10进制。2平方++++
十六进制就是 位数值乘以16的(位数减1的幂)
2.1.2 字数据大小
一个计算机基础知识点:系统的字长指明指针数据的大小,虚拟地址也是靠其编码的,所以虚拟地址的范围为0~2^w-1. 可以去理解一下。为什么如此。
32为字长虚拟地址为4G
64字长虚拟地址为1.84*10^19字节
char 1 short 2 int 4
long 看字节大小 32为4 64 为8 char *类型也是如此
2.1.3寻址和字节顺序
大小端一个很好的记忆方法:
理解数据高位和地址低位高位就行
0x12345678 12 为数据高位 78为数据低位
大端的意思是 数据高位存储在内存的低地址
小端的意思是 数据低位存储在内存的低地址
自己理解一下加深印象
注意进行网络数据传输的时候常常有一个字节序的问题。有的计算机是小端有的计算机是大端,
当他们进行数据的交互的时候需要处理一下。
书上用指针强转举例,将int float point 类型进行强转,分别以其最低字节的地址进行二位16进制的打印,
用sizeof确定原始数据类型大小增加了代码可移植性。
这章例子中精华的一句话: 这种强制类型转化告诉编译器,程序应该把这个指针堪称指向一个字节序列,而不是指向一个原始数据类型的对象。
书上有一个图说了下浮点数在转化为十六进制的表示:
有关浮点数到十六进制的资料:https://www.cnblogs.com/caomu08/p/15161397.html
例子贴一下:
浮点数19.625用float是如何存储的:
将浮点数转换成二进制:10011.101(将 19.625 整数部分采用除 2 取余,小数部分采用乘 2 取整法);
用科学计数法表示二进制浮点数:1.0011101*2^4;
计算指数偏移后的值:127 + 4 = 131 (10000011);
拼接综上所述,float 类型的 19.625 在内存中的值为:0 - 10000011 - 001 1101 0000 0000 0000 0000。
浮点数19.625在内存中的存储方式为:0 - 10000011 - 00111010000000000000000。这个存储结构可以解释如下:
第一个位表示符号位,0表示正数。
接下来的8位(10000011)表示指数部分。在IEEE 754标准中,浮点数的指数部分采用偏移值存储,因此需要对实际指数值进行偏移。在这里,实际指数为4,偏移值为127,所以偏移后的指数部分为131(10000011)。
最后的23位(00111010000000000000000)表示尾数部分。尾数部分是对小数部分的二进制表示,其中小数点左边的第一个1通常被省略,因为在规格化表示中,尾数部分总是以1.xxxxx的形式表示,而这里的1已经隐含在计算中。
因此,浮点数19.625在内存中以IEEE 754标准的float类型存储时的二进制表示为
0 - 10000011 - 001 11010000000000000000。
土话总结一下: 开始符号位+ 指数位(阶码权重表示127+科学计数法的指数位)+科学计数法后的去掉整数位的小数位
2.1.4~2.1.5章节
1.字符类数据最后都有一个包含null字符结尾的字符数组。 strlen 所计算的长度不包含这个null
2.在机器上的机器代码表示就是 38 56 78 18 ......这类。不同机器相同代码,机器代码有不同的表示。
2.1.6 布尔代数简介
基于机器中的0和1 ,引出 运算符
取反 ~ a=0 ~a = 1
与 & a b都为1时才为1 否则为0
或 | a b中任意一个为1则为1 否则为0
异或^ a = 1 b = 0 a^b =1
(a^b ) ^a =b ;
2.1.7 C语言中的位级运算
其实就是将数表示为二进制 然后按照上一章的 ~ & | ^的规则来计算。
2.1.8 C语言中的逻辑运算
他们只表示0或者1 和上面的布尔运算的例子不同
a || b
a && b
!
有一个不同是 前面a 为 1或者 0 对于其计算的特征后面的就不校验b为0 或者1 了 ,直接判为1 或者 0 所以有些代码需要注意 写在右边的表达式不被执行
2.1.9 C语言中的移位运算
左移 像左移K位 将前面高位丢弃 并在右端补K个0
逻辑右移 在左端补k个0
算数右移 在左端补K个最高有效位的值 看前面的最高位符号位 1补K个1,0补K个0
java中
x>>k 是算术右移K位
x>>>k是逻辑位移K位
1<<2+3 <<4 移位的优先级没有 算数运算符高 1<<(2+3)<<4
2.2整数表示
B2U_w w 是它的位数 0~2^(w-1) 都有唯一一个w位数
书上列举的一个向量表示法
B2U_4(0001) = 0*2^3 +.......1*2^0 = 0+0+0+1 = 1
B2U_4(1111) = 1*2^3 +1*2^2 + 1*2^1 + 1*2^0 = 1+1+1+1 = 15
补码编码
首位为 - 后面跟整数一样的 。
补码表示一般表示有符号位,负数表示
负数的有符号转为无符号的正数会变得很大
|TMIN| = |TMax| +1
2.2.4 有符号和无符号之间得转化
unsigned 变为 int 注意位模式不变就行了 如果 无符号最高位为1 那么变为无符号就是负数。
强转注意一下 ,实际得机器级表示是一样的,解释方法不同。
当将一个有符号数映射为它相应的无符号数时,负数就变成了大的正数。
2.2.5 有符号和无符号
%d 十进制 %u 无符号表示 %x 十六进制 %p 地址 %s 字符串 %c字符
C语言特征 :
如果比较运算 < > == 中有一个时无符号一个是有符号,那么都会强制转化为无符号数,在执行运算。
-1 < 0U 左边的-1强转为一个无符号巨大的数,明显表达式错误。
2.2.6 扩展一个数字的位表示
要将无符号数扩展位一个更大的数据类型,零扩展,表示的开头加0
要将有补码数字转化为更大的数据类型,符号扩展,很好理解最高位为符号位,
不影响其实际值。
2.2.7 截断数字
减少一个数的位数。 比如说int 转为short
把它表示为二进制后 32位截断位16位 然后转一下
int x = 53191;
short sx = (short)x; /* -12345 */
int y = sx ;
2.2.8 关于有符号数与无符号数的建议
就是有符号和无符号一起运算的时候 有符号数会隐式转化为无符号数,
间接导致程序错误且很难被发现。
getpeername 的漏洞
int maxlen;
int len = KSIZE < maxlen ? KSIZE :maxlen 如果 maxlen为负数
memcpy(user_dest,kbuf,len); len为负数
void *memcpy(void *dest ,void *src,size_t n);
size_t为无符号数unsigned int的宏定义
如果传入的maxlen为负数 隐式的 有符号数转化为无符号数 就会导致越界。
2.3 整数运算
x< y ; x -y < 0 两个会有不同的结果 。 x = -1 ; y = 1 ; 第一个就不成立。
两个正数相加有可能得到一个负数。 最高位变为1 补码表示就为 负数。
2.3.1 无符号加法
x+y 大于 其数据类型的位数表示就溢出
2.3.2 补码加法
>=2^(w-1) 正溢出 符号位为0
x+ y 上下区间 正常
< - 2^(w-1) 负溢出 和上面无符号一样
2.3.3 补码的非
执行位级补码非得第一种方法是对每一位求补,在对结果加1
x -x = ~x + 1
5 -0101 = 1010 + 1
2.3.5 - 6 无符号和补码的乘法
其实就是超过其数据类型的位数就截断,然后在根据其二进制算下。
截断就是溢出了
XDR库中举例了一个乘法溢出的例子,导致数据内存分配不够。
乘以常数 除以2的幂
移位来表示和计算。
2.3.8 关于整数运算的最后的思考
1.有限字长限制了可能的值的取值范围,结果运算可能溢出。
2.注意书写整数常数,和调用库里面的函数,里面变量的定义之间的差别
是有符号还是无符号, 有符号到无符号的隐式转化需要特别注意。
2.4 浮点数
V= x*2^y 的有理数进行编码
二进制小数
101.11_2 (_2 这个打不出来 用这个_代替) = 1 * 2^2 + 0 * 2^1 + 1 * 2 ^ 0 + 1 * 2 ^(-1) + 1 * 2 ^(-2) = 4+0+1+1/2 +1/4 = 5.75
2.4.2 IEEE浮点数表示
V = (-1)^s *M * 2^E
符号 S
尾数 M
阶码 E 浮点数加权
上面举了一个例子其实已经说清楚了浮点数的编码方式了
s E(科学计数法指数值+阶码加指数值)科学计数法小数部分
当 E 不等于 0 或者 255时候就是规格化的值
非规则为0
无穷大为 255
NaN 表示 s 255 00000000 特殊值 开根号 -1
需要注意的是 32位机器的阶码基础值为127 64位的为1023
2.4.4 舍入
向偶数舍入 小数部分四舍五入
向零 去掉小数部分
向下 正数时去掉小数部分 负数的时候 -1.5 变成了 -2
向上 直接小数部分去掉 正数+1
2.4.5 浮点运算
当参数中有特殊值的时候,1/ -0 = 负无穷 ;1/ +0 正无穷
2.4.6 C语言中的浮点数
说了int float double 之间的强制转化可能导致的问题。
int > float 不会溢出但是可能舍入
int or float > double 没问题
double 转化为float 可能 溢出正无穷负无穷 E = 255
float or double > int 值会像0舍入 且有可能溢出
一位 64位浮点数转化为 16位有符号浮点数的时候 产生了溢出 为一个例子