《代码之髓》试读:4.2 if 语句诞生以前

如果没有 if 语句该如何编写程序呢?我们首先来考察一下这一问题。 为什么会有 if 语句 本章我们使用一种非常原始的程序设计语言——汇编语言。汇编语言中是没有 if 语句的,但是从 C 语言很容易就能编译成汇编语言。接下来,我们用 C 语言先编写带 if 语句的代码,再试着将其编译成汇编语言看一下 ①。 C 语言下的源代码如下所示,其含义是如果 X 等于 456 则做相应处理 ②。 C语言 int main(){ int x = 123; /* if语句前 */ if(x == 456){ /* if语句中 */ } /* if语句后 */ } 编译后输出如下汇编语言代码。 汇编语言 _main: …… movl $123, -8(%rbp)  ❶ # if语句前 movl -8(%rbp), %eax ❷ cmpl $456, %eax jne LBB1_2  ❸ # if语句中  ❹ LBB1_2:  ❺ # if语句后 …… 我们试着来解读一下:首先,把 -8(%rbp) 理解为原来代码中的 x。 ❶句把数值 123 代入 x,❷句将 x 的值移存到临时场所后,把它和数值 456 相比较。 ------------------------------------ ① 通过编译转换成汇编语言,然后汇编到机器语言,最后链接成一个可执行文件,现在一般把这一整串的动作统称为编译。这里说的编译仅指转换成汇编语言这一个步骤。 ② 实际实验的代码里,通过使用 _ _asm_ _ 在汇编语言里嵌入了注释。这里为了简洁易读做了改写。请参照本书文前的“本书构成”部分获取可执行的源代码。 ------------------------------------ 接下来的❸句是关键,它表示在前一句的比较中,如果两边不相等 则跳转至 LBB1_2 处。换句话说,如果两边相等则不跳转接着执行下面的命令。❹句就是 if 语句中的代码,它只在两边相等时被执行。不相等时程序跳转至 LBB1_2(即❺句处),❹句不被执行 ①。 这种满足条件后跳转的命令很早就有。比如 1949 年发明的 EDSAC 就 有“特定内存值大于零时跳转”和“特定内存值为负时跳转”这两条命令 ②。 为什么会有 if...else 语句 大家在学习 if 语句时,想必同时也把 else 和 else if 语句配套地学习 了吧 ③。没有 else 和 else if 程序就没法写了吗?非也!本节我们来看一下 同样没有 else 和 else if 语句的汇编语言。 汇编语言中的表达方式 我们先在 C 语言中实现 else 语句,然后把它编译成汇编语言。这是 一段处理 x 值为正为负或为零时的代码。 C语言 /* if语句前 */ if(x > 0){ /* 为正时的处理 */ }else if(x < 0){ /* 为负时的处理 */ }else{ /* 为零时的处理 */ } /* if语句后 */ 与前面一样,这段代码编译后结果如下。 --------------------------- ① 这几个命令的意思分别是:movel=move long integer,cmpl=compare long integer,jne=jump if not equal。 ② 准确来讲,不是内存而是累加器(accumulator)。本书把内存一词理解为记忆装置并用在全书中。 ③ C 语言中,else if 不是独立的语句,而是 else 后紧跟着的 if 语句。这个表达在各种语言中不尽相同,Perl 语言和 Ruby 语言中有 elsif,Sh 语言和 Python 语言中有elif 这样专门的关键字。 --------------------------- 汇编语言 _main: …… # if语句前 movl -8(%rbp), %eax cmpl $0, %eax jle LBB1_2 # 为正时的处理 ❷ jmp LBB1_5 ❸ LBB1_2: ❹ movl -8(%rbp), %eax cmpl $0, %eax jge LBB1_4 # 为负时的处理 ❻ jmp LBB1_5 ❼ LBB1_4: ❽ # 为零时的处理 ❾ LBB1_5: ⓫ # if语句后 我 们 按 顺 序 来 读 一 下: ❶ 句 指 如 果 x 小 于 或 等 于 0 时 跳 转 至 LBB1_2(❹句),❷句是为正的处理,❸句跳转至 LBB1_5(⓫句)。接 着,❺句指 x 大于或等于 0 时跳转至 LBB1_4(❽句),❻句是为负的处 理,❼句跳转至 LBB1_5(⓫句)。最后,❿句是为零的处理。A 那么,就实际中 x 为正,x 为负,x 为零的情况,我们来追踪下程 序是如何执行的。 为正时,❶句的“小于等于零则跳转”不成立,因此不跳转而继续 执行❷句中为正时的处理,然后执行❸句中跳转至⓫句,程序结束。 为负时,❶句的“小于等于零则跳转”成立,跳转至❹句,❺句的 大于等于零跳转不成立,故不跳转而继续执行❻句中为负时的处理,然 后执行❼句中的跳转至⓫句,程序结束。 为零时,❶句的“小于等于零则跳转”成立,跳转至❹句,❺句的 A jle 是 jump if less or equal 的略称,实现小于等于零时的跳转。Jge 与之相反,是 jump if greater or equal 的略称。jmp 是 jump 的略称,表示无条件跳转。 “大于等于零跳转”成立,故跳转至❽句,在❾句执行为零时的处理。 由上可见,本来要表达如果等于某值则执行某事的逻辑的,却不得 不表达为如果不等于某值则跳转至某处执行某事。如此这般条件颠倒, 看起来实在是有些混乱 ①。 C语言中的表达方式 这种不使用 else 语句的书写方式在 C 语言中不可能实现吗?不是 的。C 语言中,如果使用跳转至指定行的命令 goto 语句的话,这个一样 可以实现。实验中的实现代码如下所示。goto END; 语句意指跳转至标 示有 END: 的一行。 C语言 void not_use_if(int x){ if(x <= 0) goto NOT_POSITIVE; printf("正数\n"); goto END; NOT_POSITIVE: if(x >= 0) goto NOT_NEGATIVE; printf("负数\n"); goto END; NOT_NEGATIVE: printf("零\n"); END: return; } 使用 if...else 语句的好处 众所周知,C语言程序设计中 else 语句的使用不是必不可少的,替 代方案是使用 goto 语句。其功能是跳转至指定的某行,这很好理解。 那么,上面的代码便于理解吗?至少在笔者看来,这段代码烦杂、 结构差,如果不瞪大眼睛仔细读很难理解。我们将其和直接使用 if...else 语句的代码比较下,来看看哪种方式更便于理解。 C语言 void use_if(int x){ ------------------------------ ① 也有条件不颠倒的写法,由于篇幅所限,在此不介绍它的代码实现了。 ------------------------------ if(x > 0){ printf("正数\n"); }else if(x < 0){ printf("负数\n"); }else{ printf("零\n"); } } 导入 if...else 语句的好处正在于此。针对条件为真为假的不同情况分 流处理,这样的模式在程序设计中屡见不鲜。为了能简洁地表达并方便 轻松地阅读这种逻辑,于是引入了 if...else 语句这种新的规则。 程序设计中 else 语句并不是必需的。但是,笔者还是乐于使用 else 语句,毕竟这能让程序编写更加轻松。另外,笔者也希望别人能使用 它,毕竟这也能让理解程序更轻松。

>代码之髓

代码之髓
作者: [日] 西尾泰和
副标题: 编程语言核心概念
原作名: コーディングを支える技術 ~成り立ちから学ぶプログラミング作法
isbn: 7115361533
书名: 代码之髓
页数: 236
出品方: 图灵教育
译者: 曾一鸣
定价: 45.00元
出版社: 人民邮电出版社
出版年: 2014-8
装帧: 平装
OSZAR »