预处理 -E
只执行到预编译,默认输出至控制台
gcc -E main.c -o main.i
编译 -S
只执行到编译,默认生成汇编文本文件,后缀.s
gcc -S main.i
gcc -S main.c
汇编 -c
只执行到汇编,默认输出二进制目标文件,后缀.o
gcc -c main.s
gcc -c main.c
链接
生成可执行文件
gcc main.o -o main
gcc main.c -o main
// 自动优化编译
gcc -O main.c -o main
预处理: 加载头文件至原始程序;
编译: 编译器将文本文件hello.i翻译成汇编程序文本文件hello.s;
汇编: 汇编器将汇编程序文件翻译成机器语言指令;
链接: 链接将已经编译好的目标文件(后缀.o)合并,生成可执行文件;
获取指令: 键盘输入"./hello",字符读入寄存器,再存放至内存;
加载文件: 利用直接存储器存取技术(DMA),目标文件可不通过CPU直接从磁盘加载到主存;
程序执行: 机器指令从主存复制到寄存器并执行,执行结果从寄存器复制到显示设备,打印输出;
内存读取速度比磁盘读取快约1000万倍;
寄存器读取速度比主存读取快约100倍;
高速缓存L1和L2是一种静态随机访问存储器(SRAM)。L1高速缓存读取速度约等于寄存器,比L2高速缓存快5倍; L2高速缓存读取速度比主存读取快5~10倍;
防止硬件被失控的程序滥用;
向应用程序提供简单一致的机制控制复杂的低级硬件设备;
注:基于进程、虚拟内存、文件实现
进程
是操作系统对正在运行的程序的一种抽象。
并发
运行是指一个进程的指令和另一个进程的指令交错执行,多个进程同时活动。一个CPU通过上下文切换
实现并发执行多个程序。
一个进程由多个线程
组成,每个线程都运行在进程的上下文中,共享同样的代码和数据。
虚拟内存是主存和I/O设备
的抽象,它为每个进程提供一种独立占用内存的假象。
每个进程看到的内存都是一致的,称为虚拟地址空间。
虚拟地址空间包括:只读的代码和数据、读/写数据、堆、共享库、栈、内核虚拟内存。
文件就是字节序列,每个I/O设备,包括磁盘、键盘、显示设备,甚至网络都可看为文件。
并发是同时具有多个活动的系统,并行是用并发使系统运行的更快。
线程级并发: 超线程,又称为同时多线程,允许单CPU执行多个控制流的技术;
指令集并发: 早期处理器需3~4个机器周期处理一条指令,现在CPU利用流水线技术,将一条指令划分为不同步骤。这些阶段可并行操作,用来处理不同指令的不同部分。若处理器一个机器周期可执行一条以上的指令,称之为超标量处理器。
单指令、多数据并行: 特殊的硬件结构允许一条指令产生多个并行执行的操作,称之为单指令、多数据(SIMD)
并行。如并行对8对单精度浮点数做加法。
字长指明指针数据的标称大小,虚拟地址以一个字长编码。字长决定了最大的寻址空间,即决定了虚拟地址空间的有效值。
如对于32位计算机,最大寻址空间 2 32   b y t e s = 4   G B \sf{2^{32}\,bytes = 4\,GB} 232bytes=4GB。
大端/小端法分别以最高/最低有效字节在最前面(地址较小)的方式存储数据。
假设 i n t \sf{int} int型变量 x \sf{x} x位于地址 0 x 100 \sf{0x100} 0x100处,其十六进制值为 0 x 12345678 \sf{0x12345678} 0x12345678,以大端法和小端法的存储格式分别如下:
逻辑右移
左端补0,算数右移
左端补符号位。
如有符号数-8,补码表示为1111 1000,算数右移1位(等价于除2),得到1111 1100,即-4。
对向量 x ⃗ = [ x w − 1 , x w − 2 , ⋯   , x 0 ] \vec x=[x_{w-1},x_{w-2}, \cdots, x_0] x=[xw−1,xw−2,⋯,x0],则位向量到整数的映射
无符号编码( b i n a r y   t o   u n s i g n e d \sf{binary \, to \, unsigned} binarytounsigned):
B 2 U w ( x ⃗ ) = ∑ i = 0 w − 1 x i 2 i B2U_w(\vec x)=\sum_{i=0}^{w-1} x_i2_i B2Uw(x)=i=0∑w−1xi2i
反码编码( b i n a r y   t o   t w o ′ s   c o m p l e m e n t \sf{binary \, to \, two's \,complement} binarytotwo′scomplement),正数反码与原码相同,负数反码是对其原码除符号位逐位取反,则:
B 2 O w ( x ⃗ ) = − x w − 1 ( 2 w − 1 − 1 ) + ∑ i = 0 w − 2 x i 2 i B2O_w(\vec x)=-x_{w-1}(2^{w-1}-1) + \sum_{i=0}^{w-2} x_i2_i B2Ow(x)=−xw−1(2w−1−1)+i=0∑w−2xi2i
补码编码( b i n a r y   t o   t w o ′ s   c o m p l e m e n t \sf{binary \, to \, two's \,complement} binarytotwo′scomplement),正数补码与原码相同,负数补码为其补码加1,则:
B 2 T w ( x ⃗ ) = − x w − 1 2 w − 1 + ∑ i = 0 w − 2 x i 2 i B2T_w(\vec x)=-x_{w-1}2^{w-1} + \sum_{i=0}^{w-2} x_i2_i B2Tw(x)=−xw−12w−1+i=0∑w−2xi2i
对整数 x x x,其向量表示 x ⃗ = [ x w − 1 , x w − 2 , ⋯   , x 0 ] \vec x=[x_{w-1},x_{w-2}, \cdots, x_0] x=[xw−1,xw−2,⋯,x0],有以下转换:
补码转为无符号数
由 B 2 U w ( x ⃗ ) − B 2 T w ( x ⃗ ) = x w − 1 2 w B2U_w(\vec x) - B2T_w(\vec x)=x_{w-1}2^w B2Uw(x)−B2Tw(x)=xw−12w,且 B 2 T w ( x ⃗ ) = x B2T_w(\vec x)=x B2Tw(x)=x,得 B 2 U w ( x ⃗ ) = x w − 1 2 w + x B2U_w(\vec x)=x_{w-1}2^w+x B2Uw(x)=xw−12w+x,得
T 2 U w ( x ) = B 2 U w ( x ⃗ ) = x w − 1 2 w + x T2U_w(x)=B2U_w(\vec x)=x_{w-1}2^w+x T2Uw(x)=B2Uw(x)=xw−12w+x
无符号数转为补码
由 B 2 U w ( x ⃗ ) − B 2 T w ( x ⃗ ) = x w − 1 2 w B2U_w(\vec x) - B2T_w(\vec x)=x_{w-1}2^w B2Uw(x)−B2Tw(x)=xw−12w,且 B 2 U w ( x ⃗ ) = x B2U_w(\vec x)=x B2Uw(x)=x,得 B 2 T w ( x ⃗ ) = x − x w − 1 2 w B2T_w(\vec x)=x-x_{w-1}2^w B2Tw(x)=x−xw−12w,得
U 2 T w ( x ) = B 2 T w ( x ⃗ ) = x − x w − 1 2 w U2T_w(x)=B2T_w(\vec x)=x-x_{w-1}2^w U2Tw(x)=B2Tw(x)=x−xw−12w
C语言中有符号数与无符号数运算时,有符号数会强制转为无符号数,比较大小时可能出错!负值赋给无符号数,自动执行T2U转换。
short v1 = -12345;
unsigned short u = (unsigned short)v1;
short v2 = (short)u;
cout << u << ' ' << v2 << endl;
//53191 -12345
// 无符号数与有符号数计算,有符号数会强制转为无符号数
unsigned int a = 0;
int b = -1;
cout << (a > b) << endl;
// 0
// 无符号数溢出
cout << a - 1 << endl;
// 4294967295
无符号数扩充位使用零扩展,补码数扩充位使用符号扩展。
位扩展时,先转换大小再转换符号,如short v = -12345,则(unsigned)v = 4294954951。
无符号数截断时,丢弃截断位;补码数截断时,丢弃截断位并将结果进行 U 2 T k U2T_k U2Tk转换。
short v = -12345;
unsigned u1 = v;
unsigned u2 = (unsigned)(int)v;
unsigned u3 = (unsigned)(unsigned short)v;
cout << u1 << ' ' << u2 << ' ' << u3 << endl;
// 4294954951 4294954951 53191
cout << (short)u1 << ' ' << (short)u2 << ' ' << (short)u3 << endl;
// -12345 - 12345 - 12345
int v1 = (1<<16) + (1<<15);
cout << short(v1) << endl;
// -32768
无符号数加法
x   + w u   y = { x + y , x + y < 2 w 正 常 x + y − 2 w , x + y ≥ 2 w 溢 出 x \, +_w^u \, y= \begin{cases} x+y, & \text x + y < 2^w & \quad正常\\ x + y - 2^w, & \text x+y \geq 2^w & \quad溢出 \end{cases} x+wuy={x+y,x+y−2w,x+y<2wx+y≥2w正常溢出
补码加法
x   + w t   y = { x + y − 2 w , x + y ≥ 2 w − 1 正 溢 出 x + y , - 2 w − 1 ≤ x + y < 2 w − 1 正 常 x + y + 2 w , x + y < − 2 w − 1 负 溢 出 x \, +_w^t \, y= \begin{cases} x+y-2^w, & \text x + y \geq 2^{w-1} & \quad正溢出 \\ x + y, & \text -2^{w-1} \leq x +y < 2^{w-1} & 正常 \\ x+y+2^w, & \text x+y< -2^{w-1} & \quad负溢出 \end{cases} x+wty=⎩⎪⎨⎪⎧x+y−2w,x+y,x+y+2w,x+y≥2w−1-2w−1≤x+y<2w−1x+y<−2w−1正溢出正常负溢出
无符号数乘法
x ∗ w u y = ( x ⋅ y ) m o d    2 w x *_w^u y = (x \cdot y) \mod 2^w x∗wuy=(x⋅y)mod2w
补码乘法
x ∗ w t y = U 2 T w ( ( x ⋅ y ) m o d    2 w ) x *_w^t y = U2T_w((x \cdot y) \mod 2^w) x∗wty=U2Tw((x⋅y)mod2w)
无符号和补码乘法的位级等价性
由 x ′ = T 2 U w ( x ) = x + x w − 1 2 w x'=T2U_w(x)=x+x_{w-1}2^w x′=T2Uw(x)=x+xw−12w, y ′ = T 2 U w ( y ) = y + y w − 1 2 w y'=T2U_w(y)=y+y_{w-1}2^w y′=T2Uw(y)=y+yw−12w,故
( x ′ ⋅ y ′ ) m o d    2 w = [ ( x + x w − 1 2 w ) ⋅ ( y + y w − 1 2 w ) ] m o d    2 w = [ x ⋅ y + ( x w − 1 y + y w − 1 x ) 2 w + x w − 1 y w − 1 2 2 w ] ) m o d    2 w = ( x ⋅ y ) m o d    2 w \begin{aligned} (x' \cdot y') \mod 2^w & = [(x+x_{w-1}2^w) \cdot (y+y_{w-1}2^w)] \mod 2^w \\ & = [x\cdot y + (x_{w-1}y+y_{w-1}x)2^w+x_{w-1}y_{w-1}2^{2w}]) \mod 2^w \\ & = (x \cdot y) \mod 2^w \end{aligned} (x′⋅y′)mod2w=[(x+xw−12w)⋅(y+yw−12w)]mod2w=[x⋅y+(xw−1y+yw−1x)2w+xw−1yw−122w])mod2w=(x⋅y)mod2w
带权重 2 w 2^w 2w和 2 2 w 2^{2w} 22w的项取模运算均为0。
乘法优化
整数乘法指令相当慢,约10个机器周期,一般分解为加法、减法、位级运算和移位运算。
对于乘法x*K,K的二进制是一组0和1交替的序列,如14可写为[0…01110]。
考虑一组从位置n到m的连续1(对于14,n=3,m=1),可用下列方式计算乘积:
\quad 形式A:(x << n) + (x << (n-1) + ··· + (x << m))
\quad 形式B:(x << (n + 1) - (x << m))
如x * 14 = (x << 4) - (x << 1),只需要两次移位和一次减法。
整数除法比整数乘法更慢,约30个机器周期。
向下舍入: 对于任意实数a,定义 ⌊ a ⌋ \lfloor a \rfloor ⌊a⌋为唯一整数a’,使得 a ′ ≤ a < a ′ + 1 a' ≤ a < a' + 1 a′≤a<a′+1。
如 ⌊ 3.14 ⌋ = 3 \lfloor 3.14 \rfloor=3 ⌊3.14⌋=3, ⌊ − 3.14 ⌋ = − 4 \lfloor -3.14 \rfloor=-4 ⌊−3.14⌋=−4
向上舍入: 对于任意实数a,定义 ⌈ a ⌉ \lceil a \rceil ⌈a⌉为唯一整数a’,使得 a ′ − 1 ≤ a < a ′ a'-1 ≤ a < a' a′−1≤a<a′。
如 ⌈ 3.14 ⌉ = 4 \lceil 3.14 \rceil = 4 ⌈3.14⌉=4, ⌈ − 3.14 ⌉ = − 3 \lceil -3.14 \rceil = -3 ⌈−3.14⌉=−3
向上舍入与向下舍入的关系: ⌈ x / y ⌉ = ⌊ ( x + y − 1 ) / y ⌋ \lceil x/y \rceil=\lfloor (x+y-1)/y \rfloor ⌈x/y⌉=⌊(x+y−1)/y⌋
证明:令 x = q y + r x=qy+r x=qy+r,其中 0 ≤ r < y 0\leq r < y 0≤r<y,则 ⌊ ( x + y − 1 ) / y ⌋ = q + ⌊ ( r + y − 1 ) / y ⌋ \lfloor (x+y-1)/y \rfloor=q+\lfloor (r+y-1)/y \rfloor ⌊(x+y−1)/y⌋=q+⌊(r+y−1)/y⌋。当 r = 0 r=0 r=0时, ⌈ x / y ⌉ = q \lceil x/y \rceil=q ⌈x/y⌉=q;当 r > 1 r>1 r>1时, ⌈ x / y ⌉ = q + 1 \lceil x/y \rceil=q+1 ⌈x/y⌉=q+1。因此,实现了向上舍入。
无符号除法(向下舍入): 对于无符号数 x w x_w xw和 k k k,且 0 ≤ k < w 0 \leq k < w 0≤k<w,则逻辑移位 x > > k = ⌊ x / 2 k ⌋ x >> k = \lfloor x/2^k\rfloor x>>k=⌊x/2k⌋。
证明:
令 x = [ x w − 1 , x w − 2 , ⋯   , x 0 ] x=[x_{w-1},x_{w-2},\cdots,x_0] x=[xw−1,xw−2,⋯,x0]、 x ′ = [ x w − 1 , x w − 2 , ⋯   , x k ] x'=[x_{w-1},x_{w-2},\cdots,x_k] x′=[xw−1,xw−2,⋯,xk]、 x ′ ′ = [ x k − 1 , x k − 2 , ⋯   , x 0 ] x''=[x_{k-1},x_{k-2},\cdots,x_0] x′′=[xk−1,xk−2,⋯,x0],其中 x x x、 x ′ x' x′、 x ′ ′ x'' x′′均为无符号数。
则 x = 2 k x ′ + x ′ ′ x=2^kx'+x'' x=2kx′+x′′, 0 ≤ x ′ ′ < 2 k 0 \leq x'' < 2^k 0≤x′′<2k,得 x > > k = ⌊ x / 2 k ⌋ = x ′ x>>k= \lfloor x/2^k\rfloor=x' x>>k=⌊x/2k⌋=x′。
补码除法(向下舍入): 对于补码 x w x_w xw和无符号数 k k k,且 0 ≤ k < w 0 \leq k < w 0≤k<w,则算术移位 x > > k = ⌊ x / 2 k ⌋ x >> k = \lfloor x/2^k\rfloor x>>k=⌊x/2k⌋。
证明:
令 x = [ x w − 1 , x w − 2 , ⋯   , x 0 ] x=[x_{w-1},x_{w-2},\cdots,x_0] x=[xw−1,xw−2,⋯,x0]、 x ′ = [ x w − 1 , x w − 2 , ⋯   , x k ] x'=[x_{w-1},x_{w-2},\cdots,x_k] x′=[xw−1,xw−2,⋯,xk]、 x ′ ′ = [ x k − 1 , x k − 2 , ⋯   , x 0 ] x''=[x_{k-1},x_{k-2},\cdots,x_0] x′′=[xk−1,xk−2,⋯,x0],其中 x x x、 x ′ x' x′为补码数, x ′ ′ x'' x′′为无符号数。
则与无符号数类似,有 x = 2 k x ′ + x ′ ′ x=2^kx'+x'' x=2kx′+x′′, 0 ≤ x ′ ′ < 2 k 0 \leq x'' < 2^k 0≤x′′<2k,得 x > > k = ⌊ x / 2 k ⌋ = x ′ x>>k= \lfloor x/2^k\rfloor=x' x>>k=⌊x/2k⌋=x′。
补码除法(向上舍入): 对于补码 x w x_w xw和无符号数 k k k,且 0 ≤ k < w 0 \leq k < w 0≤k<w,则算术移位 ( x + ( 1 < < k ) − 1 ) > > k = ⌈ x / 2 k ⌉ (x +(1<<k)-1)>> k = \lceil x/2^k\rceil (x+(1<<k)−1)>>k=⌈x/2k⌉。
证明:
由 ⌈ x / y ⌉ = ⌊ ( x + y − 1 ) / y ⌋ \lceil x/y \rceil=\lfloor (x+y-1)/y \rfloor ⌈x/y⌉=⌊(x+y−1)/y⌋,令 y = 2 k y=2^k y=2k,得 ⌊ ( x + 2 k − 1 ) / 2 k ⌋ = ⌈ x / 2 k ⌉ \lfloor (x+2^k-1)/2^k \rfloor=\lceil x/2^k\rceil ⌊(x+2k−1)/2k⌋=⌈x/2k⌉
即 ( x + ( 1 < < k ) − 1 ) > > k = ⌈ x / 2 k ⌉ (x +(1<<k)-1)>> k = \lceil x/2^k\rceil (x+(1<<k)−1)>>k=⌈x/2k⌉。
V = ( − 1 ) s × M × 2 E V=(-1)^s \times M \times 2^E V=(−1)s×M×2E
符号s
决定正负数;尾数M
是二进制小数,范围是 1 ∼ 2 − ϵ 1 \sim 2- \epsilon 1∼2−ϵ 或 1 ∼ 1 − ϵ 1 \sim 1 - \epsilon 1∼1−ϵ;阶码E
用于对浮点数加权。
对于单精度32位浮点数,符号s占1位、阶码占k=8位、尾码占n=23位。
8位阶码字段 e x p = e 7 ⋯ e 0 exp=e_7 \cdots e_0 exp=e7⋯e0编码阶码 E E E,23位小数字段 f r a c = f 22 ⋯ f 0 frac=f_{22} \cdots f_0 frac=f22⋯f0编码尾数M。
根据阶码域exp的不同,可分为下列情况:
非规格化(阶码全0)
阶码 E = 1 − ( 2 k − 1 − 1 ) = − 2 k − 1 + 2 E=1- (2^{k-1}-1)=-2^{k-1}+2 E=1−(2k−1−1)=−2k−1+2,尾数 M = f M=f M=f,用来表示0以及靠近0的数。
最小非规格数尾码 M = 2 − n M=2^{-n} M=2−n, V = 2 − n − 2 k − 1 + 2 V=2^{-n-2^{k-1}+2} V=2−n−2k−1+2。
最大非规格数尾码 M = 1 − 2 − n M=1-2^{-n} M=1−2−n, V = ( 1 − 2 − n ) × 2 − 2 k − 1 + 2 V=(1-2^{-n}) \times 2^{-2^{k-1}+2} V=(1−2−n)×2−2k−1+2。
规格化值(阶码非全0或非全1)
阶码 E = e x p − ( 2 k − 1 − 1 ) E=exp-(2^{k-1}-1) E=exp−(2k−1−1),尾数 M = 1 + f M=1+f M=1+f,其中尾数加1用于获得额外精度。
最小规格化数阶码 E = − 2 k − 1 + 2 E=-2^{k-1}+2 E=−2k−1+2 、尾码 M = 1 M=1 M=1,值 V = 2 − 2 k − 1 + 2 V=2^{-2^{k-1} + 2} V=2−2k−1+2。
最大规格化数阶码 E = 2 k − 1 − 1 E=2^{k-1}-1 E=2k−1−1、尾码 M = 2 − 2 − n M=2-2^{-n} M=2−2−n,值 V = ( 1 − 2 − n − 1 ) × 2 2 k − 1 V=(1-2^{-n-1}) \times 2^{2^{k-1}} V=(1−2−n−1)×22k−1。
特殊值(阶码全1)
尾数域全0时,无穷大;尾数域非0时,NaN(Not a Number)。
非规格化数分布在0附近,浮点数并非均匀分布,约靠近原点处约稠密。
整数 12345 12345 12345转为单精度浮点数,二进制数为【11 0000 0011 1001】,小数表示为 1.1000000111001 × 2 13 1.1 0000 0011 1001 \times 2^{13} 1.1000000111001×213,因此存储为: