C语言杂记 -- 简陋的<深入理解计算机系统>笔记
程序的表示
l 32位64位操作系统是由CPU寄存器的位数决定,即虚拟寻址的范围为2^32、2^64。
l 字节的大端小端法是以字节为基本单位的:比如十进制的7在十六位机器上表示
·
|
地址 |
100 |
101 |
|
大端法 |
00000000 |
00000111 |
|
小端法 |
00000111 |
00000000 |
l 大部分编译器默认进行算数右移和用补码表示复数
l 对于移位k来说,实际移位量为k mod 2^w(w为所移动数据类型的位数)
l 对于整型常量来说c编译器都是先将其看成正数,如果前面有 - 运算符则对其去负即转换成补码表示。
l //这造成了C中及其隐晦的存在
l //limits.h
l #define INT_MAX 2147483647 // 二进制表示方法为0111 1111 1111 1111,为C最大表示的整型数2^31 - 1
l #define INT_MIN (-INT_MAX - 1)
l /*
l 为什么不直接写成-2147483648呢? 这是因为编译器在看到这个常量时,不会先看取负元算符,而是看到了2147483648即2^31,编译器会感觉到无法用int型来表示,便会将其用长整形来表示为unsigned int 1000 0000 0000 0000,这样它的类型就为unsigned,在你进行使用的时候便会发生错误,考虑如下代码
l */
l #include <limits.h>
l #include <stdio.h>
l #include <stdlib.h>
l
l int main()
l {
l int a = 5;
l
l printf("%d", a > -2147483648); // result is 0, 这是因为比较过程中发生了隐式转换,转换成了unsigned int 之间的比较。
l printf("%d", a > INT_MIN); // result is 1,两者都是int类型,所以比较成功
l return 0;
l }
这就是C语言中,负数最小值为-2147483648,却不能直接写出的原因。具体请参考http://www.cnblogs.com/Jack47/archive/2013/01/06/TMin32-in-c.html
l 浮点数:
n 32位浮点数由一位符号位s,8位阶码exp即权值,23位尾数
|
数值类型 |
s |
exp |
尾数f |
|
规格化 |
s |
!=0 && != 255 |
f(尾数定义为1+f), 阶码为exp - 01111111 |
|
非规格化 |
s |
0000 0000 |
尾数为f(尾数定义为f, exp为1 - 01111111)原因见下 |
|
无穷大 |
s(1,0分别表示正负无穷大) |
1111 1111 |
000000000000000....... |
|
NaN(NOT a NUMBER) |
s |
1111 1111 |
!=0 |
n 原因保证最小规格化数到最大规格化数的平稳过渡,这样最小规格化数的exp为0000 0001 - 0111 1111 = -126;最大非规格化数的exp为1 - 0111 1111 = -126. 这样就保证了二进制小数数值均匀的接近于零
n 64位浮点数一位符号位s, 11位阶码, 52位尾数, 原理相同.
n 浮点数的舍入(多采用向偶数舍入, 又称其为向最接近的值舍入): 当值不位于要舍入的中间值时, 向最近的整数舍入; 当值为中间值时, 向最接近的偶数舍入. 比如1.5舍入为整数, 因为其位于一和二中间, 向偶数舍入, 为2.
汇编指令(ATT格式)
l 知识准备:32位系统寄存器
|
名称 |
特殊作用 |
|
|
%eax |
通常作为函数返回值, |
低十六位组成%ax, %ax中高八位为%ah,低八位为%al |
|
%ecx |
同上 |
|
|
%edx |
同上 |
|
|
%ebx |
同上 |
|
|
%esi |
只有低十六位的 |
|
|
%edi |
同上 |
|
|
%esp |
栈指针 |
同上 |
|
%ebp |
帧指针 |
同上 |
l 操作数(R表示取寄存器的值, M(addr)表示对地址addr的引用
|
类型 |
格式 |
操作数值 |
例子 |
|
立即数 |
$imm |
imm |
push $imm: 入栈imm |
|
寄存器 |
%e |
R[e] |
push %eax: 寄存器%eax的值入栈 |
|
存储器 |
(imm) |
M[imm] |
push (4): 地址4处的值入栈 |
|
存储器 |
(%e) |
M[R[e]] |
push (%eax): 以%eax的值为地址的值入栈 |
|
存储器 |
imm(%e) |
M[R[e] + imm] |
... |
|
存储器 |
(Ea, Eb) |
M[R[Ea]+ R[Eb]] |
... |
|
存储器 |
imm(Ea, Eb) |
M[R[Ea]+ R[Eb] + imm] |
... |
|
存储器 |
(, Ei, s) |
M[R[Ei] * s] |
... |
|
存储器 |
(Eb, Ei, s) |
M[R[Eb]+ R[Ei] * s] |
... |
|
存储器 |
imm(Eb, Ei, s) |
M[R[Eb]+ R[Ei] * s + imm] |
最常用 |
l 数据传送指令:MOV(S, Z), S和D不能同时为存储器目标
n Mov[b, w, l]:分别传送[字节, 字, 双字],
n Movb S, D: 将S传送到D
n Movs[bw, bl, wl]: 符号扩展
n Movz[bw, bl, wl]: 零扩展
n Pushl: 双字入栈, eg, push %eax
n Popl: 双字出栈, eg, pop %eax
l 算术和逻辑操作
n Leal S, D: &SàD
n INC D: D++
n DEC D: D--
n DEG D: -D
n NOT D: ~D (取补)
n ADD S, D: D = D + S
n SUB S, D: D = D - S
n IMUL S, D: D = S * D
n XOR S, D: D = D ^ S (异或)
n OR S, D: D = D | S
n AND S, D: D = D & S
n SAL(SHL) k, D: D = D << k
n SAR k, D: D = D >> k (算数右移)
n SHR k, D: D = D >> k (逻辑右移)
l 特殊的算数操作
n imull S: 某个寄存器 = S * R[%eax] (有符号)
n mull S: (无符号)
n cltd S: R[%eax] 进行符号扩展到某个寄存器
n idivl S: 有符号除法, R[%eax] / S; 商放到%eax中, 余数放到%edx中
n divl S: 无符号除法
l 控制:条件, 循环
n 条件(根据条件码来设置)
u cmp[b, w, l]: 分别比较字节, 字, 双字. cmp S1 S2(基于 S2 - S1), 会设置条件码
u set[e(equal), n(not), s(负数), g(great), l(less), a(above), b(below)] D: 根据条件码集合将D设置为1或0.
u test S1, S2(基于S1 & S2, 只改变条件码, 通常用来测试S1 ?= S2)
u j[…与set相同, 额外有mp]: jmp LABLE(直接跳转到LABLE), jmp *op(间接跳转到对op解引用后, 以解引用后的值进行跳转)
u 条件,循环控制多借助于上述组合来使用:eg
u test %edx, %eax
u je .L3 如果相等则跳转到.L3
u .L3
u 伪代码
u cmp %edx, %eax
u jne .L3 如果不相等则跳转到.L3
u .L3
u 伪代码
n 条件传送指令: 有利于现代处理器更好的执行, 避免分支惩罚
u 类似于 x < y ? y - x : x - y, 语句可以产生条件传送指令
u 配合上文的条件码, cmov[与set相同], cmovl S, R 若小于则传送
n switch语句配合跳转表来使用, 只进行一次条件判断
n call Label(* Operand): 过程调用, 将返回地址入栈, 并跳转到被调用过程的起始地址
n ret: 与call指令作用相反
优化程序性能
l 消除循环的低效率
n 对于循环中的过程调用尽量移出循环外, 例如:
n for (i = 0; i < strlen(s); i++) //strlen()函数为线性增长,在字符串长度很大时,很消耗系统资源
n 减少不必要的存储器引用, 将存储器引用储存在临时变量中.
l 处理器优化: 即充分利用存储器流水线操作的吞吐量
n 循环展开, 减少读写相关, 即所使用的数据必须等待上一次操作完成.
n 重新结合变换, 减少读写相关, eg
n for ()
acc = (acc OP data[i]) OP data[i + 1]
// acc = acc OP (data[i] OP data[ i + 1] 相比上一行, 处理器可以更新acc的同时进行(data[i] OP data[i + 1]), 而上一行就必须等待acc更新完之后才能进行(acc OP data[i])操作.
l 利用局部性原理: 高速缓存
n 时间局部性: 被引用过一次的存储器位置可能在不远的时间内继续被引用
n 空间局部性: 如果一个存储器位置被引用了, 很可能在不远的将来被引用其附近的存储器位置
n 每一级缓存只是关心其上下级缓存的分组情况, 缓存管理可能是硬件本身, 或者软件, 亦或是二者的结合
l 内核为每个进程维护一个描述符表, 描述符表(每个进程独有)是一个结构指针数组, 每个指针指向打开文件表, 打开文件表(包含当前文件位置, 引用计数等, 所有进程共享)包含一个指向v_node表的指针, v_node表也是包含一些文件基本信息(所有进程共享). 值得注意的是, 内核默认为每个进程打开标准输出, 标准输入, 标准错误输出三个文件, 如果任何一个打开文件表的引用计数变为零, 内核则会收回打开文件表中此文件的内存, 在对其引用可能会发生意想不到的错误. 子进程与父进程不同且继承的是描述符表, 同时系统借助描述符表和虚拟存储器来实现文件共享.
C语言杂记 -- 简陋的<深入理解计算机系统>笔记的更多相关文章
- 深入理解计算机系统(2.5)------C语言中的有符号数和无符号数以及扩展和截断数字
上一篇博客我们讲解了计算机中整数的表示,包括无符号编码和补码编码,以及它们之间的互相转换,个人觉得那是非常重要的知识要点.这篇博客我们将介绍C语言中的有符号数和无符号数以及扩展和截断数字. 1.C语言 ...
- 深入理解计算机系统(4.1)---X86的孪生兄弟,Y86指令体系结构
引言 各位猿友们好,计算机系统系列很久没更新了,实在是抱歉之极.新的一年,为了给计算机系统系列添加一些新的元素,LZ将其更改为书的原名<深入理解计算机系统>.这本书非常厚,而且难度较高,L ...
- 《深入理解计算机系统》 Chapter 7 读书笔记
<深入理解计算机系统>Chapter 7 读书笔记 链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(货被拷贝)到存储器并执行. 链接的时机 编译时,也就是 ...
- 【转】《深入理解计算机系统》C程序中常见的内存操作有关的典型编程错误
原文地址:http://blog.csdn.net/slvher/article/details/9150597 对C/C++程序员来说,内存管理是个不小的挑战,绝对值得慎之又慎,否则让由上万行代码构 ...
- 《深入理解计算机系统》C程序中常见的内存操作有关的典型编程错误
对C/C++程序员来说,内存管理是个不小的挑战,绝对值得慎之又慎,否则让由上万行代码构成的模块跑起来后才出现内存崩溃,是很让人痛苦的.因为崩溃的位置在时间和空间上,通常是在距真正的错误源一段距离之后才 ...
- CSAPP(深入理解计算机系统)读后感
9月到10月8号,包括国庆七天,大概每天5小时以上的时间,把Computer System: A Programmer Perspective 2rd version(深入理解计算机系统)的英文版啃完 ...
- 深入理解计算机系统(1.1)------Hello World 是如何运行的
上一篇序章我谈了谈 程序员为啥要懂底层计算机结构 ,有人赞同也有人反对,但是这并不影响 LZ 对深入理解计算机系统研究的热情.这篇博客以案例驱动的模式,通过跟踪一个简单 Hello World 程序的 ...
- 《深入理解计算机系统》【PDF】下载
<深入理解计算机系统>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382303 内容提要 本书主要介绍了计算机系统的基本概念,包 ...
- linux内核分析 第7章读书笔记——《深入理解计算机系统》
第七章 链接 --<深入理解计算机系统> 链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或拷贝)到存储器并执行. 一.编译器 大多数编译系统提供编译驱动 ...
随机推荐
- 服务器端javascript——Rhino和Node
Node: Node是v8 javasript解析器的一个特别版本,侧重于异步I/O,网络和HTTP 入门见:http://www.cnblogs.com/wishyouhappy/p/3647037 ...
- Tuna项目总结
从8.19—9.13日一共四周的时间,我在Tuna项目组进行的我的第一次正式工作,以及学习.在此,我对这个阶段的工作及学习进行一个总结,主要分为对流程的理解和对自动化测试的应用两个方面. 在总结着两点 ...
- winPcap_3_获取设备列表
获取设备列表 int pcap_findalldevs_ex ( char * source, //The 'source' is a parameter that tells the functio ...
- 利用C++11的function和bind简化类创建线程
问题引出 当在类中需要创建线程时,总是因为线程函数需要定义成静态成员函数,但是又需要访问非静态数据成员这种需求,来做若干重复性的繁琐工作.比如我以前就经常定义一个静态成员函数,然后定一个结构体,结构体 ...
- centOS 6.4挂载centOS分区
今天想用centOS打开在windows下编辑的emacs笔记,发现好像不可以自动挂载nfts分区,搜了一下,发现一大坨,还是发个文来标记下好: 首先,安装rpmforge软件库的源 命令行下输入下面 ...
- IT码农哥放弃50万年薪:辞职卖咖喱凉皮(背后深藏功与名)_互联网的一些事
IT码农哥放弃50万年薪:辞职卖咖喱凉皮(背后深藏功与名)_互联网的一些事 IT码农哥放弃50万年薪:辞职卖咖喱凉皮(背后深藏功与名)
- 转:有关Java泛型的类型擦除(type erasing)
转载自:拈花微笑 自从Java 5引入泛型之后,Java与C++对于泛型不同的实现的优劣便一直是饭后的谈资.在我之前的很多training中,当讲到Java泛型时总是会和C++的实现比较,一般得出的结 ...
- webservice(CXF)基于3.1.1版本实例
引言 有没有一种办法可以实现跨应用程序进行通信和跨平台进行通信呢? 换句话说,就是有什么办法可以实现我的应用程序 A 可以和应用程序 B 进行通信呢? 或者说是,我用 Java 写的应用程序和用 . ...
- vbs自学备份
最小化所有窗口: Set obj = CreateObject("Shell.Application")obj.MinimizeAll 最小化某窗口: Const strTitle ...
- POJ3422 Kaka's Matrix Travels 【最大费用最大流】
Kaka's Matrix Travels Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8006 Accepted: ...