分类: LINUX
2012-01-07 12:20:11
以下是我自己读CS:APP做的笔记,为原创。如果转载请注明文章出处,谢谢
第一章 计算机系统漫游
1. 信息 = 位 + 上下文 p1
2. 搜有信息均由位组成。每8个位被组织成一个分组,成为字节。区分这些字节意义的方法位它们所处于的上下文。p2
3. 文件就是字节序列,仅此而已。只由ASCII字符构成的文件成为文本文件,所有其它文件都被成为二进制文件。p2,p13
4. 编译系统包括:预处理器,编译器,汇编器,链接器。p3
5. 每键入一个字符,程序先将其读入寄存器,在放入存储器中;输出到显示器时,字符由主存读入寄存器,再由寄存器复制到显示设备(这个过程由相关CPU指令完成)。p17
6. 操作系统内核是应用程序和硬件之间的媒介。它提供三个基本的抽象:1)文件是对I/O设备的抽象;2)虚拟存储器是对主存和磁盘的抽象;3)进程是对处理器、主存和I/O设备的抽象。p18
第二章 信息的表示和处理
1 w位补码表示中最高位的权重为-2^w,第i位的权重为2^i。p40
2 处理相同字长的有符号数和无符号数之间相互转换的一般规则是:数值可能会变,但是位模式不变。p45
3 C语言执行一个运算时,如果它的一个运算数是有符号的而另一个是无符号的,那么C语言会隐式地将所有有符号参数强制转换为无符号数,并假设这两个数都是非负的,来执行这个操作。这种方法对于算术运算来说并无多大差异,但是对于像小于或大于这样的关系运算符来说,它会导致非直观的结果。p48
4 当把short转换成unsigned int时,首先进行short到int的转换,然后再由int转换为unsigned int。即(unsigned)sx <=> (unsigned)(int)sx。其中,sx为short型变量。p51
5 无符号运算可以视作一种模运算形式。无符号加法等价于计算和再模2^w。可以通过简单的丢弃x+y的第w+1位来计算这个数值(实际上发生溢出)。p54
6 两个数的w位补码之和与无符号之和有完全相同的位级表示。实际上,大多数计算机使用相同的机器指令来执行无符号或有符号的加法运算。p57
7 溢出是由计算机运算的有限性造成的。补码加法溢出规则:正溢出的结果减2^w,负溢出的结果加2^w。如w=4,-8 + -5 = -13(+16) = 3。5 + 5 = 10(-16) = -6。根据阿贝尔群性质,(x+y)-y 恒等于x,无论是否有溢出发生。p58
8 由于补码表示范围的非对称性,在函数的测试过程中,要注意测试TMin。p96
9 快速求取补码真值的一种方法:求其逆元的值再加符号。如0xFFFFFFFA,逆元的值为6(取反末位加1),所以真值为-6。p60
10 在大多数机器上,整数乘法指令MUL相当慢,需要10个或者更多的始终周期,然而其他整数运算如加、减、位级运算和移位只需要一个时钟周期。因此,编译器使用了一项重要的优化,试着用移位和加法的组合来代替乘以常数因子的乘法。假设x*14,利用等式14=2^3+2^2+2^1,编译器会将乘法重写为(x<<3)+(x<<2)+(x<<1),实现了将一个乘法替换为三个移位和两个加法。大多数编译器只在需要少量移位、加法和减法就足够的时候才使用这种优化。p63
11 在大多数机器上,整数除法要比整数乘法更慢,需要30个或更多的时钟周期。无符号和补码数的分别使用逻辑右移和算术右移运算来达到目的,这也正是为什么大多数机器上提供这两种类型的右移。不幸的是,这种方法不能推广到除以任意常数。同乘法不同,我们不能用除以2的幂的除法来表示除以任意常数k的除法。p64
12 int、float、double之间进行强制类型转换时,程序改变数值和位模式的规则。p78
13 一个检查给定地址单元中字节的函数:p28
void show_bytes(unsigned char *start, int len){
int i;
for(i = 0; i < len; i++){
printf("%.2x\n", start[i]);
}
}
------------------------------------------------------------------------------------------------------------
备注:
1. int + int,int + u_int,u_int + u_int编译器生成的汇编指令完全一样,都是ADD指令。
2. 有符号变量与无符号变量移位操作的区别:(省略-3存入内存的指令)
可以看出,不同类型变量的移位操作对应于不同类别的移位指令。SAR为算术右移,SHR为逻辑右移。
3. 栈是由加载程序使用系统调用mmap()创建的到磁盘的匿名映射。其大小可以由ulimit -s命令查看。至于栈的初始数据不是零的原因是由于栈在创建后main()函数开始前这部分内存被使用过,所以不能简单的认为匿名映射初始页面的数据值就是零。
4. 访问OS未分配的线性地址会发生段错误,用户请求增加或释放线性地址是通过系统调用完成的。malloc是glibc库函数,在用户与OS中间做了一层。也就是说,用户并非直接与OS交互,而是malloc作为代理向OS请求资源,再由malloc根据申请的资源响应用户需求。用户free后,资源只是还给了malloc。malloc是不是,或者什么时候把这部分资源归还给OS,由具体实现而定。所以,访问free掉得内存,有时候会段错误,有时候不会。
第三章 程序的机器级表示
1 虽然C语言提供了一种抽象模型,可以在存储器中声明和分配各种数据类型的对象,但是机器代码只是简单地将存储器看成一个很大的、按字节寻址的数组。C语言中的聚合数据类型,例如数组和结构体,在机器代码中用连续的一组字节来表示。即使是标量数据类型,汇编代码也不区分有符号或无符号整数,不区分各种类型的指针,甚至不区分指针和整数。
先写这么多吧,以后接着写:)