Trade Off

supercalifragilisticexpialidocious

CSAPP学习笔记第一辑

第一章

一个C语言程序从源代码阶段到可执行阶段需要经历这几个步骤: 源文件—->经过预处理器(cpp)成为.i文件—->经过编译器(cc1)成为.s文件—->经过汇编器(as)成为.o文件,所有函数调用都成为.o这样可重定向的目标程序—->最后由连接器(ld)链接成成为可执行文件。cc1这名字真好听:)

系统设计者的主要目标就是让信息在各种设备之间“拷贝速度”尽量快。显示一行“hello,world”的过程主要是从磁盘—->主存—->显示设备传递。

文件是对I/O设备的抽象表示,虚拟内存主要是对贮存和磁盘I/O设备的抽象表示,进程则是对处理器、主存和I/O设备的抽象表示。

一个进程下的多个线程共享这个进程的代码和全局数据,多线程之间比多进程之间更容易共享数据,因此线程一般比进程高效。

虚拟地址空间由多个区(area)构成:程序代码和数据、堆、共享库、栈、内核虚拟存储器(占有1/4/)。

第二章

计算机使用二进制对所有数据进行编码。(这就是为什么学计算机的同学们能够用两只手数到1024而非计算机专业的同学们只能数到10:)

这里有三种最重要的数字编码:无符号(unsigned)、二进制补码(two’s-complement)、浮点数(floating-point)。

大多数计算机采用8位的块(或者叫字节)作为最小的可寻址存储器单位。存储器就象是一个很大的数组,每一个元素就是一个字节。

十六进制的转化:把每个十六进制数分别转化为二进制即可。例如:

1
2
3
4
5
6
7
0x173A4C
1:0001
7:0111
3:0011
A:1010
4:0100
C:1100

开头的0x是十六进制的标示,无需翻译。二进制转化到十六进制是要从右侧开始,每4位二进制数换成一个十六进制,因为取的是4位二进制数,所以不可能超过15,就能够换算成十六进制的0~F。

用一个数除以2或者16取出余数就能得到相应的二进制或者十六进制编码,不过要倒序书写余数!!!

对于一个字长为n位的机器而言,虚拟地址范围是0~(2的n次方)减1,一共是2的n次方字节。所以32位计算机的内存最大是2的32次方,即4GB。

C语言中的指针使用机器的全字长,即可能是32位长度,占用4个字节(32位计算机)。

可移植性的一个方面就是使程序对不同数据类型的确切大小不敏感。

不同的计算机对于字节的存放顺序不同,分为大端法(big endian)、小端法(little endian),不同的处理器可能选择不同,还有一些处理器用哪种取决于CPU加电启动时确定的字节顺序,随机的:)比如0x01234567这个数字,大端法机器会这样存放:01、23、45、67。而小端法机器会这样存放:67、45、23、01(每一组数据还是正常的读法,只是要从右边开始读不同的组:)

~a不是a在|运算下的逆元(相信学完离散数学就知道什么意思了:)

是不是很……?!

交换两个数的值(技巧性炫耀,没有性能上提升):

1
2
3
4
5
6
void swap(int x , int y)
{
   x = x ^ y;
   y = x ^ y;
   x = x ^ *y;
}

移位运算要注意结核性,x<<j<<k的结核性是(x<<j)<<k,而且还要注意优先级,1<<5-1应该是这样计算的:1<<(5-1)!!!所以只要不确定就加上括号,这样肯定安全,而且可读性也是比较好的。

右移运算分两种:算术右移和逻辑右移。算术右移在左边补充n个最高有效位的拷贝,逻辑右移是在左边补充n个0.对于无符号数的右移必须是逻辑的(补0),而几乎所有的编译器/机器组合都对有符号数据使用算术右移(拷贝最高有效位)

(此处略去xxx行,实在不想学习各种数字的定义和表示方法,承认不愿意钻研数学。不过虽然数学很难懂,但数学是死的,今天学一点明天就会少一点:)

Comments