第四周(9.28-10.04):
学习计时:共5小时 读书: 代码: 作业: 博客: |
一、学习目标 |
1. 理解二进制在计算机中的重要地位
2. 掌握布尔运算在C语言中的应用
3. 理解有符号整数、无符号整数、浮点数的表示
4. 理解补码的重要性
5. 能避免C语言中溢出,数据类型转换中的陷阱和可能会导致的漏洞
|
二、学习资源 |
(提示:可选项,如有其他相关资源请在此说明):
1. 教材:第二章《信息的表示和处理》,详细学习指导见这。 2. 课程资料:https://www.shiyanlou.com/courses/413 实验三,课程邀请码:W7FQKW4Y 3. 教材中代码运行、思考一下,读代码的学习方法见这。
|
三、学习方法 |
(提示:为提高学生的学习效果,请在此处为学生提出微课程学习的具体要求或建议)
1. 进度很重要:必须跟上每周的进度,阅读,练习,问答,项目。我会认真对待每一位同学,请你不要因为困难半途而废。 2. 问答很重要:遇到知识难点请多多提问,这是你的权利更是您对自己负责的义务。
3. 实践很重要:解决书中习题,实践书中实例,完成每周项目,才算真的消化了这本好书。通过实验楼环境或自己安装的虚拟机在实践中进行学习
4. 实验报告很重要:详细记录你完成项目任务的思路,获得老师点评和帮助自己复习。
|
四、学习任务 |
(提示:请将要求学生完成的任务、测验或思考题列在此处) 1. 阅读教材,完成课后练习(书中有参考答案) 2. 考核:练习题把数据变换一下 3. 加分题:课后作业最多两人一组,互相不能重复,1星题目每人最多加一分,2星题目每人最多加二分,3星题目每人最多加三分,4星题目每人最多加四分。
|
五、后续学习预告(可选): |
教材第三章《程序的机器级表示》 |
六、学习过程 |
笔记:CPU处理东西的时候用的是二进制的 基本功:进制转换 类型 任务:ALU:A+L L: and or not————Nand 与非 A:加法器(补码) 把减法改成加法——溢出 逻辑运算的与非门 数.{有符号数 /{无符号数 \{浮点数
考试重点: 重点题目: 2.4、2.6、2.8、2.11、2.13、2.14、2.18、2.19、2.21、2.23、 2.24、2.25、2.27、2.29、2.33、2.34、2.39、2.40、2.42、2.43、 2.44、2.45、2.47、2.50、2.52、2.54
第二章 信息的表示和处理· 三种数字:无符号数、有符号数(2进制补码)、浮点数 · 溢出:计算机的表示法是用有限数量的位来对一个数字编码,当结果太大以至不能表示时,会溢出 · 整数运算:编码的数值范围较小,精确;浮点运算:数值范围较大,近似,不可结合
1. 信息存储 ①、1个字节=8位,大多数计算机将1个字节作为最小的可寻址的存储器单位。(单片机除外) ②、机器级程序将存储器(一般指内存)视为一个非常大的字节数组,称为虚拟存储器。 ③、存储器的每个字节由一个唯一的数字标识,称为地址,所有可能地址的集合称为虚拟存储空间。 ④、每台计算机都有一个字长:指明整数和指针数据的标称大小,决定了虚拟存储空间的最大值,即决定了寻址范围。 ⑤、大小端: 大端法:高字节在低位,低字节在高位。 小端法:低字节在低位,高字节在高位。 现在许多处理器都使用双端法,即用户可以通过配置来决定使用大端存储还是小端存储。 PS 有一点需要注意的是: UDP/TCP/IP协议规定网络字节序是大端法。进行网络编程的时候,发送数据时需要将本地字节序转换成网络字节序,接收到数据后需要将网络字节序转换成本地字节序。
2.移位运算 1.左移<< 2.右移>> 逻辑右移:在左端补k个0,多用于无符号数移位运算 算术右移:在左端补k个最高有效位的值,多用于有符号数移位运算。 · Java中用>>表示算术右移,用>>>表示逻辑右移 · 移位运算优先级小于算术运算
3.C语言中的有符号数和无符号数 无符号常量:后缀字符U或u 1.转换原则:底层的位保持不变 (1)有符号数→无符号数 非负数——保持不变 负数——转换成大正数 (2)无符号数→有符号数 以2^*(w-1)为界限: 小于它——保持不变 大于它——转换为负数值 [0,2^(w-1))范围内的数字,无符号和补码表示相同;范围之外的,需要加上或者减去2^w 2.运算时若同时存在有符号数和无符号数,会隐式的将有符号数强制类型转换为无符号数,并且假设这两个数都是非负的。
4.无符号加法 考虑两个非负整数 x 和 y,满足 0 ≤ x, y ≤ 2 w -1。每个数都能表示为 w 位无符号数字。然而,如果计算它们的和,我们就有一个可能的范围 0 ≤ x + y ≤ 2 w+1 -2。表示这个和可能需要 w + 1 位。
5.函数 getpeername 的安全漏洞 对用户来说,大多数内核维护的数据结构应该是不可读的,因为这些数据结构可能包含其他用户和系统上运行的其他作业的敏感信息,但是显示为 kbuf 的区域是用户可以读的。 参数 maxlen 给出的是分配给用户的缓冲区的长度,这个缓冲区是用参数user_dest 指示的。然后,第 16 行的计算确保复制的字节数据不会超出源或者目标缓冲区可用的范围。 不过,假设有些怀有恶意的程序员在调用 copy_from_kernel 的代码中对 maxlen 使用了负数值,那么,第 16 行的最小值计算会把这个值赋给 len,然后 len 会作为参数 n 被传递给 memcpy。 不过,请注意参数 n 是被声明为数据类型 size_t 的。这个数据类型是在库文件stdio.h 中(通过 typedef)被声明的。典型地,在 32 位机器上被定义为 unsigned int。 既然参数 n 是无符号的,那么 memcpy 会把它当作一个非常大的正整数,并且试图将这样多字节的数据从内核区域复制到用户的缓冲区。 虽然复制这么多字节(至少 2 31 个)实际上不会完成,因为程序会遇到进程中非法地址的错误,但是程序还是能读到没有被授权的内核存储器区域。 我们可以看到,这个问题是由于数据类型的不匹配造成的 : 在一个地方,长度参数是有符号数 ; 而另一个地方,它又是无符号数。 正如这个例子表明的那样,这样的不匹配会成为缺陷的原因,甚至会导致安全漏洞。幸运的是,还没有案例报告有程序员在 FreeBSD 上利用了这个漏洞。 他们发布了一个安全建议, “FreeBSD-SA-02:38.signed-error” ,建议系统管理员如何应用补丁消除这个漏洞。 要修正这个缺陷,只要将 copy_from_kernel 的参数 maxlen 声明为类型 size_t,也就是与 memcpy 的参数 n 一致。同时,我们也应该将本地变量 len 和返回值声明为 size_t。 我们已经看到了由于许多无符号运算的细微特性,尤其是有符号数到无符号数的隐式转换,会导致错误或者漏洞的方式。 避免这类错误的一种方法就是绝不使用无符号数。实际上,除了 C以外,很少有语言支持无符号整数。 很明显,这些语言的设计者认为它们带来的麻烦要比益处多得多。例如,Java 只支持有符号整数,并且要求用补码运算来实现。 正常的右移运算符 >> 被定义为执行算术右移。特殊的运算符 >>> 被指定为执行逻辑右移。 当我们想要把字仅仅看做是位的集合,并且没有任何数字意义时,无符号数值是非常有用的。例如,往一个字中放入描述各种布尔条件的标记(flag)时,就是这样。 地址自然地就是无符号的,所以系统程序员发现无符号类型是很有帮助的。当实现模运算和多精度运算的数学包时,数字是由字的数组来表示的,无符号值也会非常有用。
6.扩展一个数字的位表示 一种常见的运算是在不同字长的整数之间转换,同时又保持数值不变。 当然,当目标数据类型太小以至于不能表示想要的值时,这根本就是不可能的。 然而,从一个较小的数据类型转换到一个较大的类型,这应该总是可能的。 将一个无符号数转换为一个更大的数据类型,我们只需要简单地在表示的开头添加 0,这种运算称为零扩展(zero extension) 。 将一个补码数字转换为一个更大的数据类型可以执行符号扩展(sign extension) ,规则是在表示中添加最高有效位的值的副本。 由此可知,如果我们原始值的位表示为 [x w-1 ,x w-2 ,…,x 0 ],那么扩展后的表示就为[xw-1 ,…,x w-1 ,x w-1 ,x w-2 ,…,x 0 ]。
7.截断:减少表示一个数字的位数。而这么做可能会改变它的值,这也是溢出的一种形式。 将一个w位的数截断为k位数字时,就会丢弃高w-k位。 · 对于无符号数来说,就相当于 mod 2的k次幂 · 对于有符号数来说,先按照无符号数截断,然后再转化为有符号数
8.舍入 舍入:浮点运算只能近似的表示示数运算想要找到最接近x的值就是舍入,问题的关键在于在两个可能的值中间确定舍入方向。 向偶数舍入:也叫向最接近的值舍入。是默认方法。将数字向上或向下舍入使的结果的最低有效数字是偶数。其他三种方式产生实际值的确界。
9.IEEE浮点表示 IEEE浮点标准: 用V=(-1)^s*M*2^E来表示一个数: 符号:s决定这个数是正还是负。0的符号位特殊情况处理。 阶码:E对浮点数加权,权重是2的E次幂(可能为负数) 尾数:M是一个二进制小数,范围为1~2-ε或者0~1-ε(ε=1/2的n次幂
10浮点运算 1.浮点加法 浮点加法是可交换的 浮点加法不具结合性 大多数值的浮点加法都有逆元,除了无穷和NaN。 浮点加法满足单调性 2.浮点乘法 浮点乘法是可交换的 浮点乘法不具有结核性 浮点乘法的单位元为1.0 浮点乘法在加法上不具备分配性 在一定条件下满足单调性
11.C语言中的浮点数 int、float、double的相互转换 int → float 不会溢出但有可能舍入 int/float → double 结果保留精确数值 double → float 可能溢出为±∞,由于精确度较小也有可能被舍入 float/double → int 向零舍入,可能溢出。
|
七、遇到的问题及解决 |
1、float的k=8, n=23。 bias = 2^7 - 1 = 127。 最小的正非规格化数为2^(1-bias-n) = 2^-149。 最小的规格化数为2^(0-bias)*2 = 2^-126。 最大的规格化数(二的幂)为2^(2^8-2 - bias) = 2^127。 因此按各种情况把区间分为[TMin, -148] [-149, -125] [-126, 127] [128, TMax]。 float fpwr2(int x) 1、未解决//无法理解答案 2、这一章公式较多,理解困难。 解决:我认为公式还是比较偏向练习掌握了解。掌握部分重点,比如移位运算和强制类型转换等;其他要用到的时候可以再查阅资料即可。 |
八、其他 |
关于加法器的减法转为加法运算是由溢出来达成的认知更加清晰了。 加深对CPU处理东西的时候用的是二进制的理解。 |