第一章是绪论,总体讨论了计算机系统的实现方式,所以将两章合并到一起写
题外话,《深入理解计算机系统》是卡内基.梅隆大学计算机本科的教材。不知道卡内基梅隆的可以Google下,计算机专业应该在全美能排上前三了,在这里感谢一下卡内基和梅隆这两位商业巨富,有了你们的无私奉献精神,才有了今天美国的富强。这本书很有名气,也买了很久了,期间粗略的翻阅过其中一些章节,一直没有系统的钻研过。借着考SA的机会,彻底通读一遍此书。软考中的知识点有组成原理,在选书的过程中,总觉得现在一些将组成原理的书写的太过枯燥了,也许是本人偏向软件(或者说对硬件一窍不通)的缘故。《深入理解计算机系统》不但详细讲解了计算机系统的组成,CPU的原理(这个有点偏向于体系结构),更为难能可贵的是,本书对硬件与软件的结合非常好,第二章《程序的机器级表示》,就是在讲解汇编语言的同时,把相应的汇编代码对应的C程序有机的结合起来,读起来兴趣盎然。如果有和我一样偏软件的却想重温组成原理和体系结构同学,强烈推荐你们看一下。
本章主要是介绍计算机中数据是怎样存储的。先介绍几个基本的单位:
1. 位bit(b): 表示一个二进制数,最小的可寻址单位
2. 字节byte(B):计算机中处理数据的基本单位,由8个二进制位组成
3. 字: 总线传送的字节块
4. 字长: CPU一次处理数据的位数。 字中的字节数/整数或指针的标准值,决定了虚拟存储空间的大小
5. 总线: 被设计成传送定长的字节块
编译器把文本文件编译成可执行文件的过程:
.c文件->cpp预处理器(.i文件)->ccl编译器(.s文件)->as汇编器(.o文件,可重定位)->ld链接器(可执行文件a.out)
其中,可重定位的文件,在后续的《Linkers and Loaders》笔记中详细描述,敬请关注。
控制器:在设备本身上或主板的芯片组
适配器:插在主板上插槽上的卡
内存:DRAM
高速缓存:SRAM
存储器层次结构:
寄存器->一级缓存->二级缓存->内存->硬盘->网络服务器
编译器维护各种数据类型的信息,但实际生成的可执行程序却没有类型信息。也就是说CPU只是处理二进制,具体如何处理,要看根据实际代码生成的汇编文件来决定。故而编译器对类型的判断与转换至关重要,编程时切记要关注编译器关于类型转换的告警,否则类型错误的问题很不好查。
C语言中对类型范围的定义:
C语言对不同数据类型设置了下界,却没有设置上界
|
保证的 |
典型32位机 |
||||
|
最小值 |
最大值 |
字节数 |
最小值 |
最大值 |
字节数 |
Char |
-127 |
127 |
1 |
-128 |
127 |
1 |
Unsigned char |
0 |
255 |
1 |
0 |
255 |
1 |
Short |
-32767 |
32767 |
2 |
-32768 |
32767 |
2 |
Unsigned short |
0 |
65535 |
2 |
0 |
65535 |
2 |
Int |
-32767 |
32767 |
2 |
-2147483648 |
2147483647 |
4 |
Unsigned int |
0 |
65535 |
2 |
0 |
4294967295 |
4 |
Long |
-2147483647 |
2147483647 |
4 |
-2147483648 |
2147483647 |
4 |
Unsigned long |
0 |
4294967295 |
4 |
0 |
4294967295 |
4 |
<limits.h>限定了机器的类型取值范围,可以通过INT_MAX,INT_MIN,UINT_MAX,UINT_MIN…这些宏,来获取当前环境(CPU,编译器)的C类型大小
下面是典型的32位机器和64位机器不同类型的具体大小。这里说“典型”,指的是并非所有32位和64位机器都是如此定义,编译器不同,具体的大小也不同。本人就在TC的环境下发现,int居然是2个字节的,不知道为何如此设计。
C类型 |
典型32位机 |
典型64位机 |
Char |
1 |
1 |
Short |
2 |
2 |
Int |
4 |
4 |
Long |
4 |
8 |
Pointer |
4 |
8 |
Float |
4 |
4 |
double |
8 |
8 |
字节顺序和大小端(big/little endian):
big/little endian这个计算机术语,出自《格利弗游记Gulliver's Travels》,就好比我国的《镜花缘》。书中描述了两个国家,这两个国家为了吃鸡蛋时先敲碎鸡蛋的大端还是小端争论不休,从而演变为连年的战争,其实是讽刺当年英法之间的百年战争。在学习技术的过程中穿插一些典故轶事,也颇为有趣
大小端实际上是指CPU的制造厂商对于存放数据的一种不同形式。这两种形式在效率上没有差别,由于没有标准规定,所以造成了当今两种不同的实现。
按照从最低有效字节到最高有效字节的顺序存储对象,叫做小端;按照从最高有效字节到最低有效字节的顺序存储对象,叫做大端
假设int i = 0x01234567,i的高位字节为0x01,低位字节为0x67;i 占用四个字节,地址为0x100-0x103,如果按照大端存放,则高位字节0x01,放入最小的地址0x100,而低位字节0x67,放入最大的地址0x103中。如下图:
大端:
低位字节 高位字节
地址 |
0x100 |
0x101 |
0x102 |
0x103 |
值 |
01 |
23 |
45 |
67 |
也就是说,数据的高位,保存在低地址;数据的低位,保存在高地址。C语言的取址符&,都是从最低的地址开始取的,所以如果打印出来,会觉得跟我们正常顺序一样,但对计算机来说,恰好是反的。
小端:
低位字节 高位字节
地址 |
0x100 |
0x101 |
0x102 |
0x103 |
值 |
67 |
45 |
23 |
01 |
常用字符的ascii值:
0-9:0x30-0x39 48-57
A-Z:0x41-0x5a 65-90
a-z:0x61-0x7a 97-122
C语言的逻辑运算:
0:False ~0:True
与AND:&&
或OR:||
非NOT:!逻辑运算是从左至右结合的
如果前面的运算能得出结果了,那么后面的运算不会继续
a && 5/a: 不会除0
p&& *p++: 不会操作空指针
C语言的位运算:
与AND:&
或OR:|
非NOT:~
异或EXCLUSIVE-OR:^ 不同为真,相同为假位运算是从左至右结合的
左移:向左移动k位,右端补k个0
右移:无符号数,逻辑右移;有符号数,可以算术右移,可以逻辑右移
1) 算术右移(绝大部分机器)
左边补最高有效位
00110011>>2 = 00 001100
11001100>>2 = 11 110011
2) 逻辑右移
左边补0
11001100>>2= 00 110011
先写到这里,接下来讨论有/无符号整数和浮点数的表示