- 持续更新
文章目录
前言
- 大一编程小白,刚学完C++基础内容,一点点的项目经验。
- 学了sql、数据结构、微信小程序、深度学习等,啥都只会一点点。
- 此博客根据csapp和哔站csapp的配套课程,写下自认为的重点和理解,内容比较草率。
- 写此博客作为对自己读薄csapp的监督。
- 欢迎大佬们对笔记内容的指正和对自己见解的分享
那我们正式开始吧!
第一章:计算机漫游
Lecture 01: Course Overview
8月25日
书籍内容
- C语言是作用于Unix的程序语言而开发出来的
-
寄存器 > 高速缓存(SRAM) > 主存(DRAM) > 本地储存(本地磁盘) > 远处储存(Web服务器)
-
利用高速缓存,可以将程序性能提升一个数量级
-
操作系统的两个功能:
- 防止硬件被应用程序滥用
- 向应用程序提供简单的机制来控制低级硬件设备
文件是对I/O设备的抽象。虚拟内存是对主存和磁盘I/O设备的抽象。进程是对处理器、主存和I/O设备的抽象
- 进程:
- 进程相当于操作系统对一个正在运行的程序的抽象
- 操作系统保存对进程运行所需的所有状态信息进行跟踪(也就是上下文)
- 操作系统内核(kernel):管理一个进程到另一个进程的转换。它是系统管理全部进程所用代码和数据结构的集合
- 线程:一个进程由多个线程组成。实现并行处理
- 虚拟内存:没搞懂
- 文件:文件就是字节序列。任何I/O设备都能看成文件。它向应用程序提供了一个统一的视图。
- Amdahl定律:当我们对系统的某个部分加速时,对其整体的影响取决于该部分重要性和加速程度
S = 1 ( 1 − α ) + α / k {\color{red}S = \frac 1{(1-\alpha)+\alpha/k}} S=(1−α)+α/k1
S :加速比(初始所需时间 T o l d / 加速后所需时间 T n e w ) α :系统某部分所需执行时间和系统执行总时间的比例。 k : 该部分性能提升比例 S:加速比(初始所需时间T_{old}/ 加速后所需时间T_{new})\\ \alpha:系统某部分所需执行时间和系统执行总时间的比例。\\ k:该部分性能提升比例 S:加速比(初始所需时间Told/加速后所需时间Tnew)α:系统某部分所需执行时间和系统执行总时间的比例。k:该部分性能提升比例
视频补充
- 整形溢出时,将会成负数
总结
- 第一章为对全书的概括与入门,因此不过深研究
第二章:信息的表示和处理
Lecture 02: Bits,Bytes,and Integers
8月27日
书籍内容
- 64位机器可运行32位
- 1字节8位,字节是最小的可寻址的内存单位,而不是位
- 指针指向的虚拟地址
- 十六进制以0x开头
- 字节储存顺序:
- 最低有效字节放在最前面:小端法(占大多数)
- 最高有效字节放在最前面:大端法
- 强制类型转换对系统级编程非常有用
- 使用typedef来给数据类型重命名,提高可读性
- int32_t:指定int为32位的
- EXCLUSIVE-OR:异或 (^)
^ | 0 | 1 |
---|---|---|
0 | 0 | 1 |
1 | 1 | 0 |
原码:最高位表示符号位,0表示正数,1表示负数,其余位表示数值大小。例如,+5的原码是00000101,-5的原码是10000101。
反码:正数的反码和原码相同,负数的反码是将原码中除符号位外的所有位按位取反。例如,+5的反码是00000101,-5的反码是11111010。
补码:正数的补码和原码相同,负数的补码是将原码中除符号位外的所有位按位取反后加1。例如,+5的补码是00000101,-5的补码是11111011。
补码是计算机中最常用的表示带符号整数的方式,其好处是可以用同一种方式进行加减运算,且减法可以转换成加法。
————————————————
版权声明:本文为CSDN博主「牧鸯人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_61703913/article/details/129331632
- x<<k(逻辑左移):x向左移动k位,丢弃最高k位,右端补k个0
- x>>k:1. 逻辑右移:x向右移动k位,丢弃最低k位,左端补k个0
2. 算术右移:x向右移动k位,丢弃最低k位,左端补k个最高值
- 位移量应小于待位移值的位数
- 记录一下练习题2.13与其拓展:
//x:数据字 m:掩码
//bis(位设置):以x为基础,在所有m = 1的位置上,输出z = 1
//bic(位清除):以x为基础,在所有m = 1的位置上,输出z = 0
int bis(int x, int m);
int bic(int x, int m);
//或
int bool_or(int x, int y){
int res = bis(x, y);
return res;
}
//异或
int bool_xor(int x, int y){
int res = bis(bic(x, y), bic(y, x));
return res;
}
//与 这个是自己写的,不知道能不能化简
int bool_and(int x, int y){
int res = bic(bic(bis(x, y), bic(x,y)), bic(y, x));
return res;
}
- 如果对第一个参数求值就能确定表达式的结果,那么逻辑运算符就不会对第二个参数求值
- 例: a && 5 / a (a && 5)并不会受到之后运算的影响,因此不可能造成被零整除
- 仅用位级和逻辑运算,实现x,y是否相等的函数
bool is_same(auto x, auto y){
return !(x ^ y);
}
- 无符号数据类型会比有符号的最大值大一些
- 有符号数的最小值的绝对值比最大值大1
- Java只支持有符号数
- 二进制转无符号数:B2Uw ( w:长度 )
- B2U4([1011]) = 1 * 23 + 0 * 22 + 1 * 21 + 1 * 20 = 11
- 无符号编码具有唯一性,每个二进制对应唯一的无符号数
- 补码转有符号数:B2Tw (w:长度)
- B2T4([1011]) = -1 * 23 + 0 * 22 + 1 * 21 + 1 * 20 = -5
- 补码编码同样有唯一性
- Java只有补码
- 有符号与无符号之间的强制类型转换:
int a = -123;
unsigned int b = (unsigned int)a;
cout << b << endl;
//输出:4294967173
- 原因:不改变位表示,仅仅改变了二进制的转换方式
- 可以执行一个符号扩展,来保存有符号值的符号,来让补码数字转换成一个更大的数据类型
- 隐式的强制类型转换容易发生难以找到的错误!尽量少用无符号数!
int sum_element(int a[], unsigned length){
int res = 0;
for(int i = 0; i < length - 1; i++){
res += a[i];
}
return res;
}
//当length传入0时,将会出现内存错误!
视频补充
- 在访问指针前,先确保它不是空指针
总结
- 有符号数到无符号数的隐式转换容易发生错误或漏洞,尽量少用
- 在C/C++中的>>符号可能为算数右移或逻辑右移
- 字节是最小的可寻址的内存单位,而不是位
Lecture 03: Bits,Bytes,and Integers cont
8月28日
书籍内容
- 无符号数加法:
- 若两个无符号数相加后溢出,则y = y - MAX (MAX:无符号数的最大值)
-
例:(MAX = 1234)1232, 1233, 0, 1, 2
- 如何检测?
return (x + y) >= x;
- 无符号数求反:
- 加法逆操作 -x = MAX - x
- 补码加法:
- 正溢出,则y = y - MAX
- 负溢出,则y = MAX + y
-
例:(MAX = 1234)
1232, 1233,
-1234, -1233, -1232, … , 1232, 1233,-1234, -1233
- 补码的非:
- 当x > TMinw时,x补码的非即为-x
- 当x = TMinw时,x补码的非为TMinw
-
例:1011(-5) -> 0101(5), 1000(-8) -> 1000(-8)
- 推理:对每一位求补,再对结果加一([1011] -> [0100] -> [0101])
- 无符号乘法:
- 无符号数x,y实际乘积为(x * y) mod 2w(截断)
-
例:
x = [101] --> 5
y = [011] --> 3
x * y = [001111] --> 15(截断前)
x * y = [111] --> 7(截断后)
- 补码乘法:
- 同样mod 2w进行截断,然后再把无符号数转为补码
-
例(同上):
x = [101] --> -3
y = [011] --> 3
x * y = [110111] --> -9(截断前)
x * y = [111] --> -1(截断后)
位级表示是和无符号乘法相同的
- 乘以常数:
- 乘法:10+个时钟周期。除法:30+个时钟周期。其他:1个时钟周期
- 原因:计算机的乘法相当慢,因此,编译器使用了一个优化----用位移和加减法的组合来代替乘法,也就是LEA指令
- LEA原理:对一个数左移k,相当于和2k相乘
-
例:x * 14 = (x<<3) + (x<<2) + (x<<1) = (x<<4) - (x<<1)
- 除以2的幂
- 同上,可以用逻辑,算数右移来实现
- 但与乘法不同,除法只能除以二的幂,无法表示除以任何常数
视频补充
- 在64位的计算机上,最大能访问的地址为47位(2 ^ 47)~= 128 * 10 ^ 12字节(非实际)
- 即使在64位计算机上,不加限定符的int,是32位的
- 大多数计算机为小端序,但Internet是通过大端序传输的,因此在网络接口,必须进行转化
总结
- 计算机对整数的运算实际上是模运算
- 编译器通过把乘除法转换成位移和加减法来加快运行速度
Lecture 04: Floating Point
8月30日
书籍内容
- 二进制表示方法:
-
例:10.1.11 = 1 * 22 + 0 * 21 + 1 * 20 + 1 * 2-1 + 1 * 2-2 = 15 / 4
- b = ∑ i = − n m 2 i × b i m : 小数点前的位数 − n : 小数点后的位数 i : 第 i 位 b = \sum\limits_{i = -n}^{m} 2^i \times b^i\\ \space\\ m:小数点前的位数\\ -n:小数点后的位数\\ i:第i位 b=i=−n∑m2i×bi m:小数点前的位数−n:小数点后的位数i:第i位
- 二进制的小数点向左移动一位相当于这个数被2除
- 定点表示法缺点:无法有效表示非常大的小数
- IEEE浮点表示:
- V = ( − 1 ) s × M × 2 E s ( 符号 ) : 决定正负。 s = 1 ( 负数 ) , s = 0 ( 正数 ) M ( 尾数 ) : 二进制小数 E ( 阶码 ) : 对浮点数加权,权重是 2 E V = (-1)^s \times M \times 2^E\\ \space\\ s(符号):决定正负。s = 1(负数), s= 0(正数)\\ M(尾数):二进制小数\\ E(阶码):对浮点数加权,权重是2^E V=(−1)s×M×2E s(符号):决定正负。s=1(负数),s=0(正数)M(尾数):二进制小数E(阶码):对浮点数加权,权重是2E
单精度浮点(float):
s(1位)+ E(8位)+ M(23位)= 32位
双精度浮点(double):
s(1位)+ E(11位)+ M(52位)= 64位
视频补充
总结
第三章:程序的机器级表示
Lecture 05
9月1日
书籍内容
视频补充
总结
Lecture 06
9月2日