图书前言

前言Foreword全国大多数高校都把C语言或者C++语言作为学习程序设计的入门课程,一般都是在大学第一学期或者第二学期就开始进入程序设计语言的学习。在学习程序设计语言之前,大多数高校仅仅开设了“计算机导论”课程,因此初学者对计算机专业知识了解很少,在学习程序设计语言时,只能片面地、肤浅地用学习数学的方法来学习编程语言。例如,数学中的等式: 

a+b=c

在C++程序中表现为下列语句: 

a+b=c;

但在编译时就会出错: “'=' : left operand must be lvalue”。意思是说: 等号左边必须是一个值。其实这里的值就是指变量。为什么数学上可以这样写,但在C++程序中就不行呢?

显然,在C++中有等号的式子,编译器都会当成赋值语句来处理。这时,大多数读者可能就会死记这条语法规则: 在赋值语句中,等号左边必须是一个变量。为什么要求是变量?也许大多数初学者只知其然,不知其所以然。其实,对于赋值语句计算机是这样处理的: 先计算等号右边式子的值,再将值存放到等号左边表示的内存单元中。在计算机中,只有变量才会分配内存单元用于存放数据值,因此等号左边必须是一个变量。前面的语句中由于等号左边的式子a+b没有内存单元,所以编译时就会出错。程序中,只有定义了变量,操作系统才会为这个变量分配内存单元,变量才能存储数据。这也是为什么在C++程序中变量必须“先定义后使用”的原因。

又如,在学习数组时,对数组元素的访问有两种方法: 指针法和下标法。例如: int a\[10\];下面两种语句是等价的:a\[5\]=1;

(a+5)=1;◆C++程序设计前言这两种对数组元素的赋值方法,哪种执行效率高呢?

将数组元素存储在一块连续的存储空间里就可以随机地访问它们。在大多数程序设计语言中,一个具有n个元素的数组中的元素是按照0,1,…,n-1编号的。假设每个数组元素的宽度是w,那么数组a的第i个元素的开始地址为

base+i×w

其中base是分配给数组a的内存块的开始地址。也就是说,base是a\[0\]的开始地址,因此

a\[5\]的地址=a\[0\]的地址+5×4

这个计算过程需要CPU参与运算。

第二种赋值语句中,*(a+5)表示的内存地址只需要CPU中的指令指针寄存器IP向下移动5个内存块(每个内存块的宽度为一个数组元素的宽度),这个过程不需要CPU参与运算。

从以上分析可以得知,第二种赋值语句,即指针法的执行效率较高。要理解其中的道理,就必须了解计算机组成原理和编译原理的相关知识。

从上面两个例子可以知道,程序设计语言涉及专业知识非常广泛,只单一地学习计算机程序设计语言的语法知识是很难真正学懂计算机程序设计语言的。这些专业知识正是初学者所缺乏的,也正是初学者学习程序设计语言感觉非常困难的原因之一。

由于上述原因,初学者对程序设计语言的学习往往还停留在死记硬背语法规则,对程序设计语言的理解还停留在“只知其一,不知其二”的基础上。由于初学者不能很好地掌握程序设计语言的原理,对语言的“迁移”能力很差,因此大多数高校开设了多门程序设计语言课程,浪费了大量课时,这也是当前大多数程序设计课程的基本现状。因此,在学习程序设计语言之前,有必要了解程序的相关知识。

1. 程序=数据结构+算法

程序是一组指令的有效集合。数据是被程序处理的信息。由于数据在计算机中通过程序指令来处理,因此就要规定数据的组织形式——数据结构。所谓数据结构,是指相互之间存在一种或多种特定关系的数据元素的集合,它是计算机存储、组织数据的方式。每一种数据结构都有存储结构和逻辑结构。存储结构就是指数据在内存中的存储方式,逻辑结构就是指数据的组织形式。算法(algorithm)是对解题方案的准确、完整的描述,是一系列解决问题的清晰指令。通俗地说,算法就是计算方法。算法和数据结构的结合构成了程序。因此,程序也可以理解为是用一定的数据结构对算法的实现,通常用某种程序设计语言编写,运行于某种目标体系结构上。打个比方,一个程序就像一个用汉语(程序设计语言)写下菜谱(程序),用于指导懂汉语和烹饪方法的人(体系结构)来做这个菜。

2. 程序≠进程

程序是一组有序的静态指令,是一种静态的概念。所谓进程,就是程序的一次运行活动,属于一种动态的概念。进程离开了程序也就没有了存在的意义。因此,可以这样说: 程序是进程运行的静态文本,而进程是执行程序的动态过程。如果把一部动画片的电影拷贝比拟成一个程序,那么这部动画片的一次放映过程就可比作一个进程。

一个进程可以执行一个或多个程序。例如,一个编译进程进行C++源程序编译时,要执行词法分析、语法分析、语义分析、代码优化、存储分配和代码生成等几个程序。反之,同一程序也可能由多个进程同时执行。例如,上述C++编译程序可能同时被几个编译进程执行,它们对相同或不同的源程序分别进行编译,各自产生目标程序。再次以动画片及其放映活动为例,一次电影放映活动可以连续放映几部动画片,相当于一个进程可以执行几个程序。反之,一部动画片可以同时在若干家电影院中放映,这相当于多个进程可以执行同一程序。不过要注意的是,几家电影院放映同一部电影,如果使用的是同一份拷贝,那么实际上是交叉进行的。但在多处理机情况下,几个进程却完全可以同时使用一个程序副本。

程序可以作为一种软件资源长期保存,而进程则是一次执行过程,它是暂时的,是动态地产生和终止的。这相当于电影拷贝可以长期保存,而一次放映活动却只延续1~2h。

进程在内存中有其相应的代码空间(代码区)和数据空间(数据区),而程序存储在机器的外部存储器中,不会占用内存空间。进程需要使用一种机构才能执行程序,这种机构称为处理机(processor)。处理机执行指令,根据指令的性质,处理机可以单独用硬件或软、硬件结合起来构成。如果指令是机器指令,那么处理机就是一般所说的中央处理机(CPU)。

3. 逻辑地址≠物理地址

任何程序在没有得到运行前,都以静态文本的形式存放在计算机的外部存储器中。这些静态文本只有逻辑地址。程序从外存调入内存运行时,要给程序分配一定的内存空间。这些内存空间都有物理地址,也就是说,程序具有了物理地址。这时,程序就变成了进程,从静态文本变成了动态程序。为了保证CPU执行指令时可以正确地访问存储单元,需将用户程序中的逻辑地址转换为运行时由机器直接寻址的物理地址,这一过程称为地址映射或者地址绑定。一个程序在运行时刻,逻辑地址空间的映像包括数据区和代码区,如第2章的图2.12所示。对地址的进一步了解可以参见2.2.2节、2.2.3节、226节、745节和9.1节。

生命在于运动,程序也是一样。只有运行中的程序才具有生命力,程序中的各种变量才会拥有内存空间,它们才会有生命周期。程序要得到运行,就得有现实的物质条件: 运行时间和内存空间。程序只有分配了物理内存空间,得到了CPU的运行时间,才能真正得以运行。算法是程序的灵魂,对算法的评价主要从时间复杂度和空间复杂度来考虑。同样,对程序的认识也应当从运行时间和内存空间两个角度去考虑。因为一个程序只有得到时间和空间两个物质条件才会得到执行,才会变得有意义。

本书以程序的运行时间和存储空间(时空)为主线,把握程序的静态性和动态性两个特点,通过“运行时序图”和“内存模型图”的分析手段,从时间和空间两个角度深入讲解程序运行时的基本原理。在讲解程序设计语言基本概念的同时,穿插讲述计算机组成原理、操作系统、编译原理、数据结构、算法设计等方面的相关知识。

C语言是面向过程的程序设计语言,C++是从C语言发展演变而来的一种面向对象的程序设计语言。那么,学习C++语言是否应该首先学习C语言呢?答案是否定的。不应该把面向过程和面向对象的程序设计对立起来,任何面向对象的程序设计都需要用到面向过程的知识。其实,C++并不是纯粹的面向对象的语言,它是一种混合语言。C++既可以编写面向过程的程序,也可以编写面向对象的程序。因此,C++语言也可以作为程序设计入门语言来学习。

本书共分3部分: 

第1部分(第1、2章): C++的基础知识。首先通过简单例子介绍一个C++程序的结构特点: 程序由若干个函数组成;然后介绍面向过程与面向对象这两种程序设计方法的区别及特点;最后介绍C++语言的字符集、标识符、关键字和数据类型。目的是让读者对C++程序设计有一个总体认识。

第2部分(第3~6章): C++面向过程的程序设计。首先介绍操作符的优先级和结合性;然后介绍3种语句——选择、循环、跳转,数组、指针和引用的使用方法;最后介绍函数。

第3部分(第7~9章): C++面向对象的程序设计。主要介绍面向对象程序设计的4个特点: 抽象、封装、继承、多态性。

本书的大多数示例都是笔者在多年教学过程中的演示程序,简单易懂。由于不同学校、不同专业对学习C++程序设计有不同的要求,因此对于学过C语言的学生,教师在使用本教材时,对第1、2部分可以只用少量课时对符号常量、引用、string类、内联函数、函数重载和函数模板等知识进行讲解,重点应放在第3部分的学习中。

本书肯定会有不妥甚至错误之处,诚请广大读者、教师不吝指正,作者将不胜感激。作者的电子邮件地址: teacherzj@126.com。