深入理解计算机系统

为什么32位系统只能支持4G内存?

因为每台计算机都有一个字长, 这也是指针的大小, 32位系统的指针字长为4字节, 也就是32位, 因此它能表示的地址为 0 ~ 2^32 - 1, 所以超出4G的内存地址, 它是无法表示的

int, long, int32_t, int64_t类型的区别?

long 类型在32位系统是4字节, 在64位是8字节, 为了不同的编译系统导致不同的分配字节, 因此引入了int32_t, int64_t类型

逻辑位移和算数位移

逻辑位移向右位移时, 高位以0补充, 算数位移向右位移高位以最高有效值补充, 一般无符号以逻辑位移方式, 有符号位移按算数位移方式

无符号与有符号进行运算

无符号与有符号数进行运算, 有符号数被会转成无符号数进行运算

1
2
3
4
5
6
7
8
int main(int argc, const char * argv[]) {
unsigned int a = 1;
int b = -2;
cout << "result: " << a + b << " 2^32: " << pow(2, 32) << " a+b-2^32: " << a + b - pow(2, 32) << endl;
//result: 4294967295 2^32: 4.29497e+09 a+b-2^32: -1

return 0;
}

unsigned 导致的BUG

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
float sum_elements(float a[], unsigned length)  
{
int i = 0;
float sum = 0;
//length为无符号 length-1的结果也是无符号, 所以不管结果是正数还是负数, 对于程序猿而言都是大于0的
for(i = 0; i <= length -1; ++i)
sum += a[i];
return sum;
}

//size_t ==> unsigned int
size_t strlen(const char *s);

int strlonger(char *s1, char *s2)
{
//因为strlen返回的结果是unsigned int 所以下面的结果都是大于0的 应改为strlen(s1) > strlen(s2)
return strlen(s1) - strlen(s2) > 0;
}

汇编

寄存器

63 31 15 7 0
%rax %eax %ax %al 返回值
%rbx %ebx %bx %bl 被调用者保存
%rcx %ecx %cx %cl 第4个参数
%rdx %edx %dx %dl 第3个参数
%rsi %esi %si %sil 第2个参数
%rdi %edi %di %dil 第1个参数
%rbp %ebp %bp %bpl 被调用者保存
%rsp %esp %sp %spl 栈指针
%r8 %r8d %r8w %r8b 第5个参数
%r9 %r9d %r9w %r9b 第6个参数
%r10 %r10d %r10w %r10b 调用者保存
%r11 %r11d %r11w %r11b 调用者保存
%r12 %r12d %r12w %r12b 被调用者保存
%r13 %r13d %r13w %r13b 被调用者保存
%r14 %r14d %r14w %r14b 被调用者保存
%r15 %r15d %r15w %r15b 被调用者保存
  • movb(传送字节)
  • movw(传送字)
  • movl(传送双字 long word)
  • movq(传送四字)
  • movs(传送单精读)
  • movl(传送双精度) 与传送双字一样 但是浮点运送用的是完全不同的指令和寄存器 因此不会有冲突

操作数指示符

类型 格式 操作数
立即数 $Imm Imm
寄存器 ra R[ra]
内存 Imm M[Imm]
内存 (ra) M[R[ra]]
内存 Imm(ra) M[Imm + R[ra]]
内存 (rb, ri) M[R[rb] + R[ri]]
内存 Imm(rb, ri) M[Imm + R[rb] + R[ri]]
内存 (, ri, s) M[R[ri] * s]
内存 Imm(, ri, s) M[Imm + R[ri] * s]
内存 (rb, ri, s) M[R[rb] + R[ri] * s]
内存 Imm(rb, ri, s) M[Imm + R[rb] + R[ri] * s]

MOV S, D

  • MOVZ 目的中剩余的z字节填充0
  • MOVS 目的中剩余的z字节填充符号位

间接引用指针实际上就是从寄存器中取出指针所保存的地址, 然后从内存找到相应的地址(%rdi); 局部变量通常是放在寄存器中;

long exchange(long* xp, long y)  {
        long x = *xp;
        *xp = y;
        return x;
}

//对应的汇编代码
// xp => %rdi  y ==> %rsi
movq    (%rdi), %rax
movq    %rsi, (%rdi)
ret

条件码

  • CF: 进位标志
  • ZF: 零标志
  • SF: 符号标志
  • OF: 溢出标志