1.什么是程序设计
程序设计是一个非常自然和直观的概念。程序仅仅是用某种具体的语言写出的一系列语句。程序随处可见,即使是在对技术充满恐惧的社会每天也会用到程序。驾驶路线说明、烹饪菜谱、足球比赛以及DNA都是形形色色的程序。计算机生来并不懂英语,它只能理解机器语言。为了指导计算机做某事,指令必须用机器语言写出。然而,通晓机器语言的人很少且机器语言难以使用——机器语言由原始的比特和字节组成,而且随着计算机体系结构的不同而变化。所以,为了用机器语言给Intel x86处理器编写一个程序,人们不得不计算好与每一条指令相对应的值,了解各条指令之间如何交互,以及许多其他底层的细节。像这样的程序设计是一项辛苦的、麻烦的工作,当然也不是凭直觉就可以完成的。
克服编写机器语言程序困难唯一需要的是一个翻译程序。汇编程序是一种形式的机器语言翻译程序——它是一个把汇编语言程序翻译成计算机可以识别的代码的程序。汇编语言没有机器语言那么神秘,因为汇编语言为不同的指令和变量命名,而不仅仅是使用一些数字。然而,汇编语言还远远不是直观的语言。汇编语言的指令名非常深奧,并且汇编语言仍然与计算机的体系结构密切相关。这意味着,正像Intel x86处理器的机器语言与Sparc处理器的机器语言不同一样,x86的汇编语言也不同于Sparc的汇编语言。利用汇编语言为某种处理器体系结构编写的任何程序在另一种处理器的体系结构上都不能运行。如果某个程序是用x86汇编语言编写的,如果想让它在Sparc的体系结构上运行则必须重写。此外,为了用汇编语言写出高效率的程序,人们还必须懂得许多关于那种处理器的底层的细节。
这些问题也可以通过另一种称为编译程序的翻译程序来进一步解决。编译程序可以把高级语言翻译成机器语言。高级语言比汇编语言更直观,并且能够翻译成适合各种不同体系结构处理器的不同类型的机器语言。这意味着如果一个程序是用高级语言编写的,则程序只需编写一次,相同的一段程序代码通过编译程序可以被编译为适用各种具体体系结构处理器的机器语言。C、C++和FORTRAN都是高级语言的例子。用高级语言编写的程序比汇编语言或机器语言可读性更好,更像英语。但是高级语言仍然必须遵守非常严格的指令规则,否则编译程序将无法理解。
2.伪代码
程序员还有另外一种形式的程序设计语言——伪代码。伪代码是用类似高级语言的一般结构形式组织起来的简单的英语。它不能被编译程序、汇编程序或者任何计算机所理解,但对程序员来说伪代码是一种非常有用的组织指令的方法。伪代码没有明确的定义。实际上,许多人写的伪代码都有一点差异。伪代码是自然语言(例如英语)和类似于C一样的高级程序设计语言之间有几分模糊不清并缺少的一环。伪代码是常见的通用程序设计概念的出色入门工具。
3 控制结构
如果没有控制结构,那么一段程序就仅仅是一系列按排列顺序执行的指令。对于简单程序来说,顺序执行方式很好,但大多数程序,像驾驶路线说明这样的例子,并不像这样简单。驾驶路线说明包括类似这样的语句:在Main Street大街上一直向前走,直到看到右边有一座教堂,如果有建筑物堵塞了道路……我们将这些语句称为控制结构,它们将程序执行的流向由简单的顺序执行改变为更复杂、更有用的流向。
lf-Then-Else
在驾驶路线说明这个例子中,MainStreet可以受结构控制。如果结果为真,就需要一组专门指令来应用于这种情况。否则,应当继续执行原来的指令集。可以用一种最自然的控制结构来解决程序中这种类型的实例:即lf-Then-Else结构通常,它看起来像:
If (condition) then
{
Set of instructions to execute ifthe condition is met;
}
Else
{
Set of instruction to executeif the condition is not met;
这里将会使用一种类C伪代码,,因此每条指令会以分号(;)结束,并且会用大括号及缩进来对指令集进行分组。前面驾驶路线说明的If-Then-Else伪代码结构看起来像这样:
Drive down Main Street;
If (street is blocked)
{
Turn right on 15th Street;
Turn left on Pine Street;
Turn right on 16th Street;
}
Else
{
Turn right on 16th Street;
}
每条指令占一行,并且各个条件指令集以大括号和缩进分组以便于阅读。在C以及许多其他程序设计语言中,Then关键字是隐含值常被省去,所以在前面的伪代码中也省略了Then。当然,其他一些程序设计语言在其语法中要求有then关键字,例如BASIC、FORTRAN,甚至Pascal。在程序设计语言中,这些类型的语法差异只是存在于表面上,它们的底层结构仍然一样。一旦程序员理解了这些语言想要传达的思想,学习各种不同的语法变体是毫不费力的。由于在随后的章节中会用到C语言,因此本书中使用的伪代码遵循类C语法规则,但是请记住伪代码能够以多种形式呈现。
另一个类C语法的常见规则是:被大括号包含的一组指令只有一条指令时,大括号是可选的。为了便于阅读,缩进指令仍然是一个不错的主意,但语法上并没有这种要求。可以遵循该规则将前面的驾驶路线说明示例重写,会产生如下的等价伪代码段:
Drive down Main Street;
If (street is blocked)
{
Turn right on 15th Street;
Turn left on Pine Street;
Turn right on 16th Street;
}
Else
Turn right on 16th Street;
这个有关指令集的规则对于本书中提到的所有控制结构都是有效的,并且规则本身也可以用伪代码描述。
If (there is only one instruction in a set of instructions)
The use of curly braces to group the instructions is optional;
Else
{
The use of curly braces is necessary;
Since there must be a logical way to group these instructions;
}
甚至对语法本身的描述也可以认为是一个简单的程序。此外,还存在着If-Then-Else结构的变体,例如Select/Case语句,但其根本逻辑是一样的:如果这件事发生了,则做这些事,否则做其他的事(甚至可以是更多的If-Then语句)。
While/Until循环
另一个基本的程序设计概念是While控制结构,它是一种循环类型。程序员经常想要多次执行同一组指令。程序可以通过循环来完成这项任务,但是它要求一组条件告诉它什么时候停止循环,以免它无限继续下去。条件为真时,While循环会指明在一个循环中执行随后的指令集。一个为饥饿的老鼠编写的简单程序如下所示:
While (you are hungry)
{
Find some food;
Eat the food;
}
老鼠仍然饥饿时,紧随while语句的两行指令会被重复执行。老鼠每次找到的食物数量可能从一小块面包屑到一整块面包不等。与此类似,While语句中指令集的执行次数的变化取决于老鼠找到的食物的多少。
While循环的另一种变体是Until循环,Until循还是Perl程序设计语言中一种有效的语法(C语言不使用这种语法)。Until循环其实是条件语句颠倒的While循环。使用Until循环的前面相同的老鼠程序如下所示:
Until (you are not hungry)
{
Find some food;
Eat the food;
}
从逻辑上来说,任何Until循环语句都可以变为While循环。前面提到的驾驶路线说明包含一条语句:在Main Street –直向前,直到看到右边有一座教堂。只需将条件颠倒,就可以将这条语句改变为一个标准的While循环。
While (there is not a church on the right)
Drive down Main Street;
For循环
另一个循环控制结构是For循环。该循环一般用于程序员想要执行确定次数的重复操作时。驾驶路线说明“沿着Destination路走5英里”可以转换成如下所示的For循环:
For (5 iterations)
Drive straight for 1 mile;
实上,For循环只不过是一个有计数的While循环。同样的语句可以像这样来写:
Set the counter to 0;
While (the counter is less than 5)
{
Drive straight for 1 mile;
Add 1 to the counter;
}
类C伪代码语法的For循环计数更加明显:
For (i=0; i<5; i++)
Drive straight for 1 mile;
在这个例子中,计数器名为i,For语句分解为由分号分隔的三部分。第一部分声明计数器并设置它的初始值,在本例中为0。第二部分像是一个使用计数器的While语句:计数器满足这个条件,就继续循环。第三部分即最后一部分说明在每次循环过程中应当对计数器采取什么动作。在这个例子中,i++是向名为i的计数器加1的简写形式。
利用所有控制结构,可以将2.1节中的驾驶路全线说明转换成如下所示的类C伪代码:
Begin going East on Main Street;
While (there is not a church on the right)
Drive down Main Street;
If (street is blocked)
{
Turn right on 15th Street;
Turn left on Pine Street;
Turn right on 16th Street;
}
Else
Turn right on 16th Street;
Turn left on Destination Road;
For (i=0; i<5; i++)
Drive straight for 1 mile;
Stop at 743 Destination Road;
更多程序设计的基本概念
我们会介绍更多一般的程序设计概念。多数程序设计语言都使用这些概念,只是在语法上稍微有些差别。本书在介绍这些概念时,会将它们融入到使用类C语法的伪代码示例中。最终,这些伪代码看起来和C语言会很像。
变量
在For循环中使用的计数器实际上是一种类型的变量。可以将变量简单地看作一个含有可变数据的对象,变量这个名字也由此而来。还有其值保持不变的变量,这些应当被称为常数。再回到汽车驾驶示例中,汽车的速度是一个变量,而汽车的颜色是一个常数。在伪代码中,变量是简单的抽象概念,但是在C语言中(并且在许多其他语言中也一样),在使用变量之前,必须定义变量并为其指定一种类型。这是因为C语言程序最后会被编译成可执行程序。像一个烹饪菜谱一样,在操作之前必须列出所有必需的配料,变量声明使得您可以做好生成程序之前的准备工作。所有变量最终}都存储在内存的某个位置,并且这些变量声明使得编译器能更高效地组织内存。但是,不论所声明变量的类型如何,它们最终只不过是内存罢了。
在C语言中,每个变量都会被指定一种类型,这个类型用于对打算存储在该变量中的信息进行说明。一些最常见的类型是int(整型值),float (浮点小数值)和char(单字符值)。只需在列出变量之前使用这些关键字即可定义变量,如下所示:
int a, b;
float k;
char z;
变量a和b现在被定义成整数,k可以接收浮点值(例如3.14),Z可以容纳一个字符值,像A或W。声明变量时或在变量声明后的任意时候,都可以使用“=”操作符给变量赋值。
int a = 13, b;
float k;
char z = 'A';
k = 3.14;
z = 'w';
b = a + 5;
上述指令执行后,变量a的值为1 3,k的值为数字3.14,Z的值为字符W,b的值为1 8,这是因为13加5等于18。变量只不过是记住值的一种方法,但是,在C语言中,必须首先声明每个变量的类型。
算术运算符
语句b=a+7是一个很简单的算术运算符的例子。在C语言中,下面的符号用于各种算术运算。
前四个运算符应该很眼熟。模减看起来像是一个新概念,但是它实际上只是取除法运算后的余数。如果a是13,然后13除以5商等于2,余数是3,即a%5=3。与此类似,因为变量a和b是整数,所以语句b=a/5会使得值2存储在b中,这是因为2是它的整数部分。要想保留更精确的结果2.6,必须用浮点类型的变量。常用运算符号如表2-1所示。
符号 |
示例 |
|
Addition |
+ |
b=a+5 |
Subtraction |
- |
b=a–5 |
Multiplication |
* |
b=a*5 |
Division |
/ |
b=a/5 |
Modulo reduction |
% |
b=a%5 |
要想在程序中使用这些概念,必须用它的语言讲述。C语言为这些算术运算符提供了若干种简写形式。其中之一在前面已经提到过,并且它常用在For循环中。简写形式如表2-2所示。
完整表达式 |
简写形式 |
说明 |
i=i+1 |
i++ or ++i |
Add 1 to the variable. |
i=i-1 |
i--or--i |
Subtract 1 from the variable. |
这些简写表达式可以与其他的算术运算符结合在一起形成更多的复杂表达式。在复杂表达式中i++和++i之间的差别就变得很明显了。第一个表达式的意思是在对算术运算符求值之后将i的值加1,而第二个表达式的意思是在对算术运算符求值之前将i的值加1。下面的示例会有助于澄清这些概念。
int a, b;
a=5;
b=a++*6;
令执行结束后,b的值是30, a的值是6,这是因为简写式“b=a十十木6;”与下面的语句等价:
b=a*6;
a=a+1;
是,如果使用了指令“b=++a*6;”,加法的次序会发生变化,,其结果与下面的指令等价:
a=a+1;
b=a*6;
因为先后顺序发生了改变,所以在这个示例中,b的值是36,a的值仍然是6。
在程序中经常发生这样的情况,需要在原变量中修改它自身的值。例如,您可能需要给变量加一个类似于12这样的任意值,并把结果存回到该变量中(例如,i=i+12)。通常这样做就可以了,而它们也有简写式,如表2-3所示。
简写形式 |
说明 |
|
i=i+12 |
i+=12 |
|
i-=12 |
Subtract some value from the variable. |
|
i=i*12 |
i*=12 |
Multiply some value by the variable. |
i=i/12 |
i/=12 |
Divide some value from the variable. |
2.4.3 比较运算符
前面介绍的控制结构的条件语句中经常用到变量。这些条件语句是以某些比较为基础的。在C语言中,这些比较运算符使用一种简写语法(如表2-4所示),这种写法贯穿了多种程序设计语言,使用相当普遍。
符号 |
示例 |
|
Less than |
< |
(a<b) |
Greater than |
> |
(a>b) |
<= |
(a<=b) |
|
>= |
(a>=b) |
|
Equal to |
(a==b) |
|
Not equal to |
(a!=b) |
这些运算符中的大多数无须解释;但是,需要注意的是“等于”的简写形式使用了双等号。这是一个重要的区别,因为双等号用于检验等值性,而单等号用于为变量赋值。语句a=7的意思是将值7置于变量a中,而a==7的意思是检验变量a是否等于7(一些程序设计语言,例如Pascal,实际上使用:=为变量赋值,以消除直观上的混淆)。也要注意的是,一个感叹号一般表示非。可以单独使用这个符号对任意表达式取反:
!(a<b) 相当于 (a>=b)
也可以使用OR和AND的简写形式将这些比较运算符串连在一起,如表2-5所示。
符号 |
示例 |
|
OR |
|| |
((a<b)||(a<c)) |
AND |
&& |
用OR逻辑连接在一起的示例语句包含两个较小的条件运算,如果a小于b或者a小于c,那么该语句就为真。与此类似,用AND逻辑连接在一起的示例语句也包含两个较小的比较运算,如果a小于b并且a不小于c,那么该语句就为真。应当用括号将这些语句分组,并且这些语句可以包含许多不同的变化。
许多事情都可以归结为变量、比较运算符和控制结构。返回到老鼠寻找食物这个示例,饥饿可以被转换为一个Boolean型的true/false变量。自然,1表示true, O表示false。
While (hungry == 1)
{
Find some food;
Eat the food;
}
这里还有另一个程序员和黑客使用得相当频繁的简写形式。C语言实际上没有任何Boolean运算符,因此任何非零值被认为是true,如果语句值为0则被认为是false。事实上,如果比较结果为true,比较运算符实际上会返回值1,如果为false,会返回值0。如果hungry等于1,则检查变量hungry是否等于1时会返回1,如果hungry等于0会返回0。因为程序仅仅使用这两种情形,所以可以将比较运算符完全去掉。
While (hungry)
{
Find some food;
Eat the food;
}
一个具有更多输入变量、更加智能的老鼠程序示范了如何将比较运算符与变量结合在一起。
While ((hungry) && !(cat_present))
{
Find some food;
If(!(food_is_on_a_mousetrap))
Eat the food;
}
这个示例假设用变量描述是否有猫存在以及食物的位置,用值1表示true,0表示false。一定要记住,任何非零值被认为是true,值0被认为是false。
未完待续!