猜想:

书中第二章很多篇幅讲了加法操作,不禁让我思考计算机执行减法,又有什么不同?

假设没有减法操作,编译源代码的时候,会将减法,改成加法的指令,比如

(1)2-4 等价与 2+(-4)

(2)那如果都是无符号数呢 2u-4u 等价于什么?或者说(1)的结论就是错的,编译程序不会这样做,不会把减法改为加法

// 源代码 demo1.c
int main(){
    unsigned char a = 2;
    unsigned char b = 4;
    unsigned char answer = a - b;  // answer <=> [0x FD] <=> [1111 1110]  <=> 254
}

// 获取汇编代码
// gcc demo1.c -o demo1.out
// objectdump -d demo1.out
00000000004004d6 <main>:
  4004d6:	55                   	push   %rbp
  4004d7:	48 89 e5             	mov    %rsp,%rbp            %rsp 栈指针,指向内存中的某个地址
  4004da:	c6 45 fd 02          	movb   $0x2,-0x3(%rbp)      (1)将局部变量a=2 [0x 02]存到内存-0x3(%rbp)中
  4004de:	c6 45 fe 04          	movb   $0x4,-0x2(%rbp)      (2)将局部变量b=4 [0x 04]存到内存-0x2(%rbp)中
  4004e2:	0f b6 45 fd          	movzbl -0x3(%rbp),%eax      (3)将a(即 0x 02)做零拓展为(0x 00 00 00 02) 存到%eax中,所以%al存储的是[0x 02] 
  4004e6:	2a 45 fe             	sub    -0x2(%rbp),%al       (4)sub 内存的-0x2(%rbp) 即[0x 04], 寄存器中的%al即[0x 02] 计算2-4并将结果存到%al
  4004e9:	88 45 ff             	mov    %al,-0x1(%rbp)       (5)将计算结果存到内存-0x1(%rbp)中
  4004ec:	b8 00 00 00 00       	mov    $0x0,%eax
  4004f1:	5d                   	pop    %rbp
  4004f2:	c3                   	retq   
  4004f3:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
  4004fa:	00 00 00 
  4004fd:	0f 1f 00             	nopl   (%rax)

对比

// 源代码 demo2.out
int main(){
    char a = 2;
    char b = 4;
    char answer = a - b;  // answer <=> [0x FD] <=> [1111 1110]  <=> -2
}

// 获取汇编代码
// gcc demo1.c -o demo2.out
// objectdump -d demo2.out
00000000004004d6 <main>:
  4004d6:	55                   	push   %rbp
  4004d7:	48 89 e5             	mov    %rsp,%rbp
  4004da:	c6 45 fd 02          	movb   $0x2,-0x3(%rbp)
  4004de:	c6 45 fe 04          	movb   $0x4,-0x2(%rbp)
  4004e2:	0f b6 55 fd          	movzbl -0x3(%rbp),%edx    2
  4004e6:	0f b6 45 fe          	movzbl -0x2(%rbp),%eax    4
  4004ea:	29 c2                	sub    %eax,%edx          sub 4, 2 (这里直接寄存器取值,做减法,有点意思,跟上面那个例子又不太一样)
  4004ec:	89 d0                	mov    %edx,%eax
  4004ee:	88 45 ff             	mov    %al,-0x1(%rbp)
  4004f1:	b8 00 00 00 00       	mov    $0x0,%eax
  4004f6:	5d                   	pop    %rbp
  4004f7:	c3                   	retq   
  4004f8:	0f 1f 84 00 00 00 00 	nopl   0x0(%rax,%rax,1)
  4004ff:	00

解析:

  1. 从第(4)步中,可以看到,sub x, y 指令对于给他的两个操作数

    不管给它的操作数是无符号还是有符号,执行的都是同样的电路操作

    https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4865b2d6-bdbd-4123-a006-8530149ec175/Untitled.png

  2. 同时也验证了猜想的不合理

思考:

  1. 上图中运算器中sub指令是如何计算的?
  2. 为什么2u-4u 和 2-4 编译出的机器代码:

参考