C语言程序设计基础实验教程
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.2 Visual C++ 6.0介绍

Visual C++ 6.0(以下简称“VC6.0”),是微软公司推出的目前使用极为广泛的基于Windows平台的可视化编程环境,由于功能强大、灵活性好、完全可扩展,以及具有强有力的Internet支持,所以从各种C++语言开发工具中脱颖而出,成为目前最为流行的语言集成开发环境之一。

利用VC6.0集成开发环境,还可以有效地实现编写及运行C语言程序。

1.2.1 安装与启动

1.安装VC6.0

计算机所需的软、硬件配置为Pentium处理器、32 MB内存或更大内存、至少200 MB的可用硬盘空间、800 px×600 px以上的显示器、Windows 98或Windows NT操作系统。安装过程如下。

(1)插入VC6.0安装光盘,打开“Visual C++ 6.0中文企业版 安装向导”对话框,如图1-1所示。

图1-1 “Visual C++ 6.0中文企业版 安装向导”对话框

(2)单击“下一步”按钮,打开“最终用户许可协议”对话框,如图1-2所示。

图1-2 “最终用户许可协议”对话框

(3)选择“接受协议”单选按钮。

(4)单击“下一步”按钮,打开“产品号和用户ID”对话框,如图1-3所示。

图1-3 “产品号和用户ID”对话框

(5)输入产品序列号、用户名及所在单位的名称。

(6)单击“下一步”按钮,打开“Visual C++ 6.0中文企业版”对话框,如图1-4所示。

图1-4 “Visual C++ 6.0中文企业版”对话框

(7)选择“安装Visual C++ 6.0中文企业版”单选按钮。

(8)单击“下一步”按钮,打开“选择公用安装文件夹”对话框,如图1-5所示。

图1-5 “选择公用安装文件夹”对话框

(9)保留默认,单击“下一步”按钮,打开“Visual C++ 6.0 Enterprise 安装程序”对话框,如图1-6所示。

图1-6 “Visual C++ 6.0 Enterprise 安装程序”对话框

(10)单击“继续”按钮进入安装过程,随后出现如图1-7所示的“Visual C++ 6.0 Enterprise 安装程序”对话框。

图1-7 “Visual C++ 6.0 Enterprise 安装程序”对话框

(11)单击“确定”按钮,搜索已安装组件并显示提示信息。

(12)单击“是”按钮,打开“Visual C++ 6.0 Enterprise 安装程序”对话框。

(13)选择“Typical”(典型安装)选项,如图1-8所示。

图1-8 选择“Typical”选项

稍后出现如图1-9所示的“Setup Environment Variables”对话框。

图1-9 “Setup Environment Variables”对话框

(14)单击“OK”按钮,提示安装进程,如图1-10所示。

图1-10 安装进程

安装进程结束后,显示如图1-11所示的“Visual C++ 6.0 Enterprise-重新启动Windows”对话框。

图1-11 “Visual C++ 6.0 Enterprise - 重新启动Windows”对话框

(15)单击“重新启动”按钮重启计算机,重启后显示如图1-12所示的“Install MSDN”对话框。

图1-12 “Install MSDN”对话框

(16)清除“安装MSDN”复选框。

(17)单击“下一步”按钮,显示如图1-13所示的提示信息。

图1-13 提示信息

(18)单击“是”按钮,打开如图1-14所示的“其他客户工具”对话框。

图1-14 “其他客户工具”对话框

(19)单击“下一步”按钮,打开如图1-15所示的“服务器安装”对话框。

图1-15 “服务器安装”对话框

(20)单击“下一步”按钮,打开“现在通过Web 注册!”对话框,如图1-16所示。

图1-16 “现在通过Web 注册!”对话框

(21)单击“完成”按钮。

2.启动VC6.0

选择“开始”→“程序”→“Microsoft Visual Studio”→“Microsoft Visual C++ 6.0”选项。

若是第1次启动,则显示“每日提示”对话框,如图1-17所示。

图1-17 “每日提示”对话框

单击“下一条”按钮,可以看到有关各种操作提示。如果清除“启动时显示提示”复选框,那么以后启动V C 6.0时将不显示此对话框。

单击“关闭”按钮关闭对话框,进入V C 6.0集成开发环境。

VC6.0集成开发环境由标题栏、菜单栏、工具栏、工作空间窗口、输出窗口、程序和资源编辑窗口、状态栏等组成,如图1-18所示。

图1-18 VC6.0集成开发环境

(1)标题栏。

标题栏位于VC6.0集成开发环境的最上方,一般放置图标、程序名称和控制按钮。

(2)菜单栏。

菜单栏是重要的人机界面的操作对象,用户通常从中选择菜单项中的命令实现所需的功能。

(3)工具栏。

工具栏是放置位图式按钮行的控制条,位图式按钮用来执行命令,单击工具栏按钮相当于选择菜单项中的命令。如果某个菜单命令具有和工具栏按钮相同的ID,那么单击工具栏按钮将会调用映射到该命令的同一个处理程序。

(4)工作空间窗口。

工作空间窗口包含用户的一些信息,如类、项目文件、资源等。右键单击其中的任何标题或图标都会弹出快捷菜单,其中包含当前状态下的一些常用操作。

(5)程序和资源编辑窗口。

在该窗口中设计和处理源程序代码和项目资源,包括对话框和菜单等,其中可以显示各种程序源代码的源文件、资源文件、文档文件等。

(6)输出窗口。

该窗口用来显示编译、调试和查询的结果,帮助用户修改用户程序的错误,提示包括错误的条数、错误位置、错误的大致原因等。

(7)状态栏。

状态栏用于显示当前操作状态、注释、文本光标所在的行列号等信息。

1.2.2 菜单及主要工作窗口

当用户使用VC6.0开发软件时,大部分的操作都通过菜单命令来完成,因此了解各个菜单命令的基本功能是非常必要的。还有一些工作窗口也是辅助程序设计的工具。

1.VC6.0的常用菜单项及其命令

(1)文件(File)菜单项。

· 新建(New):打开“新建”对话框,可以创建新的文件、工程或工作空间。

· 关闭工作空间(Close Workspace):关闭与工作空间相关的所有窗口。

· 退出(Exit):退出VC6.0集成开发环境,将提示保存窗口内容等。

(2)编辑(Edit)菜单项。

· 剪切(Cut):快捷键为Ctrl+X,将选定内容复制到剪贴板,并从当前活动窗口中删除所选内容。与“粘贴”命令联合使用可以移动选定的内容。

· 复制(Copy):快捷键为Ctrl+C,将选定内容复制到剪贴板,但不从当前活动窗口中删除所选内容。与“粘贴”命令联合使用可以复制选定的内容。

· 粘贴(Paste):快捷键为Ctrl+V,将剪贴板中的内容插入(粘贴)到当前光标指针所在的位置。注意必须先使用“剪切”或“复制”命令使剪贴板中有粘贴的内容。

· 查找(Find):快捷键为Ctrl+F,在当前文件中查找指定的字符串,可按快捷键F3查找下一个匹配的字符串。

· 在文件中查找(Find in Files):在指定的多个文件中查找指定的字符串。

· 替换(Replace):快捷键为Ctrl+H,替换指定的字符串。

· 转到(Go To):快捷键为Ctrl+G,将光标移到指定行。

· 断点(Breakpoints):快捷键为Alt+F9,打开对话框,用于设置、删除或查看程序中的所有断点。断点指示调试器暂停程序执行的位置,以查看当时的变量取值等现场情况。

(3)查看(View)菜单项。

· 工作空间(Workspace):打开工作空间窗口。

· 输出窗口(Output):打开输出窗口。

(4)工程(Project)菜单项。

· 添加到工程(Add To Project):该菜单项的子菜单中包括添加文件或数据链接等到工程之中的命令,如子菜单中的“新建”命令可用于添加“C++ Source File”或“C/C++ Header File”“文件”命令则用于插入已有的文件到工程中。

· 设置(Settings):为工程设置各种不同的选项。选择该命令后打开其中的“调试”选项卡,并通过在“Program arguments:”文本框中输入以空格分隔的各命令行参数,则可以为带参数的main函数提供相应参数(对应于“void main(int argc,char* argv[ ]){…}”形式的main函数中所需各argv数组的各字符串参数值)。注意在执行带参数的main函数之前,必须进行该设置。当“Program arguments:”文本框中为空时,意味着无命令行参数。

(5)组建(Build)菜单项。

· 编译(Compile):快捷键为Ctrl+F7,编译当前处于源代码窗口中的源程序文件,以检查是否有语法错误或警告。如果有,则显示在输出窗口中。

· 组建(Build):快捷键为F7,连接当前工程中的有关文件。若出现错误,则显示在输出窗口中。

· 执行(Execute):快捷键为Ctrl+F5,运行已经编译、连接成功的可执行程序(文件)。

· 开始调试(Start Debug):该菜单项的子菜单中包括用于启动调试器运行的命令,如“Go”命令用于从当前语句开始执行程序,直到遇到断点或遇到程序结束;“Step Into”命令开始单步执行程序,并在遇到函数调用时进入函数内部从头单步执行;“Run to Cursor”命令使程序运行到当前光标所在行时暂停其执行(使用该命令前要先将光标设置到某一个需要暂停的程序行处)。执行该命令项后启动调试器,此时菜单栏中的“调试”菜单项取代了“组建”菜单项。

(6)调试(Debug)菜单项。启动调试器后才出现该菜单项。

· Go:快捷键为F5,从当前语句启动继续运行程序,直到遇到断点或程序结束而停止(与“Build”→“Start Debug”→“Go”命令的功能相同)。

· Restart:快捷键为Ctrl+Shift+F5,重新从头开始执行程序,应在修改程序后选择该命令。

· Stop Debugging:快捷键Shift+F5,关闭调试器,中断当前的调试过程并返回正常的编辑状态,并且“组建”菜单项取代“调试”菜单项。

· Step Into:快捷键为F11,单步执行程序,并在遇到函数调用语句时进入函数内部从头单步执行(与“Build”→“Start Debug”→“Step Into”命令的功能相同)。

· Step Over:快捷键为F10,单步执行程序。执行到函数调用语句时不进入函数内部,而是一步直接执行该函数后执行函数调用语句后面的语句。

· Step Out:快捷键为Shift+F11,与“Step Into”配合使用。当进入到函数内部单步执行若干步之后发现不再需要单步调试,通过该命令可以从函数内部返回(到函数调用语句的下一语句处停止)。

· Run to Cursor:快捷键为Ctrl+F10,使程序运行到当前光标所在行时暂停其执行(使用该命令前要先将光标设置到某一个需要暂停的程序行处)。事实上,相当于设置了一个临时断点,与“Build”→“Start Debug”→“Run to Cursor”命令的功能相同。

· Insert/Remove Breakpoint:快捷键为F9,该命令在工具栏和程序文档的快捷菜单中。列在此处是为了方便读者掌握程序调试的手段,其功能是设置或取消固定断点。

(7)帮助(Help)菜单项。

通过该菜单项来查看VC6.0的各种联机帮助信息。

除了主菜单和工具栏外,VC6.0集成开发环境还提供了大量的快捷菜单,用右键单击窗口中多处都会弹出一个快捷菜单,其中包含与所选项目相关的各种命令。

2.VC6.0集成开发环境的主要工作窗口

(1)工作空间(Workspace)窗口。

工作空间窗口显示当前工作空间中各个工程的类、资源和文件信息,当新建或打开一个工作空间后,该窗口通常显示3个树视图,即ClassView(类视图)、ResourceView(资源视图)和FileView(文件视图)。如果在VC6.0企业版中打开了数据库工程,则显示DataView(数据视图)。

· “ClassView”视图:显示当前工作空间中所有工程定义的C++类、全局函数和全局变量。展开每一个类后,可以看到该类的所有成员函数和成员变量。如果双击类的名字,则打开定义这个类的文件,并把文档窗口定位到该类的定义处;如果双击类的成员或者全局函数及变量,文档窗口则会定位到相应函数或变量的定义处。

· “ResourceView”视图:显示每个工程中定义的各种资源,包括快捷键、位图、对话框、图标、菜单、字符串资源、工具栏和版本信息等。如果双击一个资源项目,则进入资源编辑状态,打开相应的资源,并根据资源的类型自动显示Graphics、Color、Dialog、Controls等停靠式窗口。

· “FileView”视图:显示隶属于每个工程的所有文件。除了C/C++源文件、头文件和资源文件外,还可以在工程中添加其他类型的文件,如Readme.txt等。这些文件对工程的编译和连接不是必须的,但将来制作安装程序时会被一起打包;同样,如果双击源程序等文本文件,则为该文件打开一个文档窗口;双击资源文件,打开其中包含的资源。

在“FileView”视图中右键单击一个工程,弹出的快捷菜单中的“Clean”命令的功能是删除全部VC6.0生成的中间文件,避免了手工删除时可能会出现的误删或漏删问题;另外某些情况下VC6.0编译器可能无法正确识别已编译的文件,以致于在建立工程时完全重建,此时使用“Clean”命令删除中间文件可以解决这一问题。

(2)输出窗口。

输出窗口中前面的4个栏最常用,在建立工程时,“Build”栏显示工程建立过程中经过的每一个步骤及相应信息。如果出现编译或连接错误,则该栏显示发生错误的文件及行号、错误类型编号和描述。双击一个编译错误,VC6.0打开相应的文件,并定位到发生错误的语句行。

工程通过编译和连接后,运行其调试版本。“Debug”栏中显示各种调试信息,包括DLL装载情况、运行时警告及错误信息、MFC类库或程序输出的调试信息、进程中止代码等。

两个“Find in Files”栏用于显示从多个文件中查找字符串后的结果,如果需要查看某个函数或变量出现在哪些文件中,则选择“Edit”→“Find in Files”命令。然后指定要查找的字符串、文件类型及路径,单击“查找”按钮,结果显示在“Find in Files”栏中。

3.调整窗口布局

VC6.0的智能化界面允许用户灵活配置窗口布局,如重新定位菜单栏和工具栏的位置。拖动菜单栏或工具栏左方类似把手的两个竖条纹放到窗口的不同位置,可以发现菜单和工具栏能够停靠在窗口的上方、左方和下方。双击竖条纹,它们还能以独立子窗口的形式始终浮动在文档窗口的上方,并且可以被拖到主窗口之外。如果有双显示器,甚至可以把这些子窗口(除了文档窗口)拖到另外一个显示器上,以进一步加大编辑区域的面积。工作空间和输出窗口等停靠式窗口(Docking View)也能以相同的方式拖动或者切换成独立的子窗口;此外,这些停靠式窗口还可以切换成普通的文档窗口模式,方法是选中某个停靠式窗口后,清除“Windows”下拉菜单中的“Docking View”复选框。

1.2.3 建立一个简单程序

1.建立并运行一个程序

这个程序的功能是向屏幕输出一个字符串“Hello World”,建立并运行程序包含如下步骤。

(1)输入并编辑程序代码。

(2)编译(生成目标程序文件.obj)。

编译就是把高级语言变成计算机可以识别的二进制语言,过程包括词法分析、语法分析、语义检查、中间代码生成、代码优化,以及目标代码生成。其中主要是进行词法分析和语法分析,又称为“源程序分析”,分析过程中发现语法错误给出提示信息。

(3)组建(生成可执行程序文件.exe)。

组建是将编译产生的.obj文件和系统库连接装配成一个可以执行的程序,在实际操作中可以直接选择“组建”从源程序产生可执行程序。将源程序转换为可执行文件的过程分为编译和组建两个独立步骤主要是因为在一个较大的复杂项目中,有多人共同完成(每个人可能承担其中一部分模块)。其中不同的模块可能用不同的语言编写,如汇编、VC或VB。有的模块可能是购买或已有的标准库模块,各类源程序都需要先各自编译为目标程序文件(二进制机器指令代码),然后通过组建程序将这些目标程序文件连接装配成可执行文件。

(4)运行(生成可执行程序文件)。

2.工程(Project)及工程工作空间(Project Workspace)

工程也称为“项目”,或“工程项目”,它具有两种含义,一是指最终生成的应用程序;二是指为了创建这个应用程序所需的全部文件的集合,包括各种源程序、资源文件和文档等,大多数较新的开发工具都利用工程来管理软件开发过程。

用VC6.0编写并处理的任何程序都与工程有关(都要创建一个与其相关的工程),而每一个工程又总与一个工程工作空间相关联。工作空间是对工程概念的扩展,一个工程的目标是生成一个应用程序,但很多大型软件往往需要同时开发数个应用程序。VC6.0集成开发环境允许用户在一个工作空间内添加数个工程,其中有一个是活动(默认)的,每个工程都可以独立地编译、连接和调试。

VC6.0通过工程工作空间来组织工程及其各相关元素,如同一个工作间(对应于一个独立的文件夹,或称“子目录”),以后程序所包括的所有的文件和资源等元素都将放入这一工作间中。从而使得各个工程之间互不干扰,使编程工作更有条理且更具模块化。最简单情况下,一个工作空间中用来存放一个工程,代表某一个要处理的程序。如果需要,一个工作空间中也可以用来存放多个工程,其中可以包含该工程的子工程或者与其有依赖关系的其他工程。

可以看出工程工作空间如同一个“盛放”相关工程所有有关信息的“容器”,创建新工程时要创建这样一个工程工作空间,而后则通过该工作空间窗口来观察与存取此工程的各种元素及其有关信息。创建工程工作空间之后,系统将创建一个相应的工作空间文件(.dsw),用来存放与该工作空间相关的信息;另外还创建其他多个相关文件,包括工程文件(.dsp)和选择信息文件(.opt)等。

编写C++程序时要创建工程,VC6.0已经预先准备了近20种不同的工程类型供选择,选择不同的类型意味着由VC6.0提前做某些不同的准备及初始化工作。例如,自动生成一个底层程序框架(称为“框架程序”),并完成某些隐含设置,如隐含位置、预定义常量、输出结果类型等。在工程类型中有一种“Win32 Console Application”类型,它是我们首先要掌握用来编写运行C++程序中最简单的一种。此种类型的程序运行时将出现并使用一个类似DOS的窗口,并提供对字符模式的各种处理与支持。实际上,提供的只是具有严格的采用光标而不是鼠标移动的界面。此种类型的工程小巧而简单,但已足以解决并支持本课程中涉及的所有编程内容与技术。可以使我们把重点放在程序的编写,而并非界面处理等方面。至于VC6.0支持的其他工程类型(其中有许多还将涉及Windows或其他编程技术与知识),有待在今后的不断学习中来逐渐了解、掌握与使用。

3.创建工程并输入源程序代码

(1)新建一个Win32 Console Application工程。

· 选择“文件”→“新建”命令,打开“新建”对话框。

· 打开“工程”选项卡,如图1-19所示。

图1-19 “工程”选项卡

· 选择“Win32 Console Application”复选框,在“位置”文本框中输入“D:\MYData\VC6”,在“工程名称”文本框中输入工程名“Sample”。VC6.0在“D:\MYData\VC6”下用该工程名“Sample”建立一个同名子目录,随后的工程文件及其他相关文件都将存放其中。

· 单击“确定”按钮,打开“Win32 Console Application - 步骤 1 共 1 步”对话框如图1-20所示。

图1-20 “Win32 Console Application - 步骤 1 共 1 步”对话框

若选择“一个空工程”单选按钮,则将生成一个空的工程;若选择“一个简单的程序”单选按钮,则生成包含一个空的main函数和一个空的头文件的工程;选择“一个"Hello World!"程序”单选按钮,则需要包含显示“Hello World!”字符串的输出语句;选择“一个支持MFC的程序”单选按钮,则可以利用VC6.0所提供的类库来编程。

· 为了更清楚地说明编程的各个环节,选择“一个空的工程”单选按钮,从一个空的工程开始。

· 单击“完成”按钮,打开“新建工程信息”对话框,如图1-21所示。

图1-22 “新建工程信息”对话框

· 单击“确定”按钮,打开“Sample - Microsoft Visual C++”窗口,如图1-22所示。

图1-22 “Sample - Microsoft Visual C++”窗口

(2)在工作空间窗口中查看工程的逻辑架构。

工作空间中的“ClassView”标签中列出的是这个工程中所包含的所有类的有关信息,因为目前程序不涉及类,所以这个标签中现在为空。打开“FileView”标签,显示这个工程所包含的所有文件信息。单击+按钮打开所有层次会发现有3个逻辑文件夹,其中“Source Files”文件夹中包含了工程中所有的源文件;“Header Files”文件夹中包含了工程中所有的头文件;“Resource Files”文件夹中包含了工程中所有的资源文件,即工程中所用到的位图和加速键等信息,如图1-23所示。

图1-23 “FileView”选项卡

逻辑文件夹是在工程配置文件中定义的,在磁盘中并没有物理地址存在,这3个逻辑文件夹是VC6.0预先定义的。就编写简单的单一源文件的C程序而言,只需要使用“Source Files”一个文件夹。

(3)在工程中新建C源程序文件并输入源程序代码。

· 选择“工程”→“添加到工程”→“新建”命令,打开“新建”对话框。

· 打开“文件”选项卡,如图1-24所示。

图1-24 “文件”选项卡

· 选择“C++ Source File”选项,在“文件名称”文本框中输入文件名“Hello.c”。

· 单击“确定”按钮,进入输入源程序的编辑窗口(注意所出现的呈现闪烁状态的输入位置光标),此时只需通过键盘输入如下源程序代码:

可通过工作空间窗口中的“FileView”标签看到“Source Files”文件夹下已有Hello.c文件,如图1-25所示。

图1-25 “Source Files”文件夹中的Hello.c文件

在工作空间窗口的“ClassView”标签中的“Globals”文件夹中也可以看到键入的main函数,如图1-26所示。

图1-26 main函数

4.不创建工程,直接输入源程序代码

在“文件”选项卡中选择“C++ Source File”选项,后续操作则与前述相同。

也可以单击工具栏中的新建文件按钮新建一个空白文件,然后单击保存按钮保存此空文件。保存时一定要以“.c”或“cpp”作为扩展名,前者为C语言源程序,后者为C++源程序;否则编译程序时自动格式化和特殊显示等很多特性将无法使用,程序无法运行。

这种方式新建的C源程序文件在编译时会提示用户,要求允许系统为其创新一个默认的工程(含相应的工作空间)。

5.编译、组建和运行程序

在编译、组建和运行程序前应保存工程,以避免程序运行时系统发生意外而使之前的工作付之东流。

选择“组建”→“编译”命令编译程序,若发现错误(error)或警告(warning),则在输出窗口中显示错误或警告所在行及具体信息,可以通过这些信息来纠正程序中的错误或警告(错误是必须纠正的,否则无法进行下一步;警告则并不影响进行下一步,当然最好处理所有的警告)。当没有错误与警告出现时,输出窗口显示的最后一行信息是“Hello.obj-0 error(s),0warning(s)”。

选择“组建”→“组建”命令组建生成可执行程序,如果出现错误,则显示在输出窗口中。组建成功后输出窗口显示的最后一行是“Sample.exe-0 error(s),0 warning(s)”。

选择“组建”→“执行”命令,VC6.0运行程序并显示运行结果,如图1-27所示。

图1-27 Hello.c程序的运行结果

其中的“Press any key to continue”由系统产生,使得用户可以浏览输出结果,直到按下任意键为止(将返回集成界面的编辑窗口处)。

选择“文件”→“关闭工作空间”命令,系统询问是否关闭所有的相关窗口。单击“是”按钮,结束一个程序从输入到执行的全过程,回到刚刚启动VC6.0的初始画面。

6.及时备份文件

(1)完全备份:将D:\myData\VC6下的文件夹Sample复制到U盘或打包成一个文件后放到个人的邮箱中。需要在其他计算机上继续完成这个工程时,将该文件夹复制到该计算机的硬盘中。启动VC6.0,选择“文件”→“打开工作空间”命令,打开这个工程即可。

(2)只备份C源程序文件:因为“Sample”工程非常简单,所以仅备份其中的C源程序Hello.c即可。需要在其他计算机上继续完成该程序时,只需将备份的程序复制到该计算机的硬盘中。启动VC6.0,根据前面的讲述,新建一个Win32 Console Application,然后通过选择“工程”→“增加到工程”→“文件”命令将Hello.cpp添加新建的工程中。

1.2.4 调试程序

1.调试程序的时机

当程序编译或者组建出错时,系统在输出窗口随时显示有关提示或出错警告信息等(如果是编译出错,只要双击输出窗口中的出错信息就可以自动跳到出错的程序行)。若编译和组建正确,而执行结果不正确,则需要使用调试工具查找程序中隐藏的出错位置(某种逻辑错误)。

以下几行程序代码编译和组建均正确,但能实现设计的要求吗?

事实上,程序设计的重点不是修正编译和组建过程中的错误,而是设计正确的算法。

2.调试程序的基本方法

(1)设置固定或临时断点。

断点是指定程序中的某一行,让程序运行至该行后暂停运行,使得程序员可以观察分析程序运行过程中的如下情况。

· 在变量(Varibles)窗口中观察程序中变量的当前值,程序员观察这些值的目的是与预期值对比,若不一致,则此断点前运行的程序肯定在某个地方有问题,以此可缩小故障范围。例如,以下程序计算cos(x)并显示,运行时发现无论x输入为多少,结果都是0.046414。

若没有看到问题所在,则应该使用调试手段定位故障位置。

· 在监控(Watch)窗口中观察指定变量或表达式的值,当变量较多时,使用监控窗口可以有目的、有计划地观察关键变量的变化。

· 在输出窗口中观察程序当前的输出与预期是否一致,若不一致,则此断点前运行的程序肯定在某个地方有问题。

· 在内存(Memory)窗口中观察内存中数据的变化,在该窗口中能直接查询和修改任意地址的数据。对初学者来说,通过它能更深刻地理解各种变量、数组和结构等是如何占用内存的,以及数组越界的过程。

· 在调用堆栈(Call Stack)窗口中观察函数调用的嵌套情况,此窗口在函数调用关系比较复杂或递归调用的情况下,对分析故障很有帮助。

(2)单步执行程序。

让程序一步一步(行)地执行,观察分析执行过程是否符合预要求。例如,以下程序预期的功能是从键盘上读入两个数xy,判断xy是否相等。相等,则在屏幕上显示x=y;不相等,则显示x<>y。程序实际的运行结构是无论输入什么数,在屏幕上总是显示x=yx<>y。通过单步执行,则能定位故障点,缩小查看的范围。例如,在单步执行的过程中输入2和3发现xy的值的确变成了2和3,此时单步跟踪却发现执行了“printf("x=y\n");”语句,因此问题出在“if(x=y)”。

在单步执行的过程中,应灵活应用“Step Over”“Into”“Step Out”‘Run to Cursor等命令,提高调试效率。建议在程序调试过程中,记住并使用这些命令的快捷键,以提高效率。

3.一个简单程序的调试过程

编写一个执行计算任务的简单程序,在已知x=3、y=5的情况下计算出xy的和s,差d,商q,模r,而后计算res=s+2d+3q+4r的值(res应该等于16)并显示在屏幕上。但编写的如下程序运行后却得出了一个错误结果res=26:

分析上述程序行,假设在要输出res结果值的倒数第2行处设置一个临时断点,让程序执行到此断点处(该行尚未被执行),然后查看此刻各变量的动态取值可能找到出错的原因。基于此分析,将光标移动到“printf("res=%d\n",res);”行处指定临时性断点的行位置。而后选择“组建”→“开始调试”→“Run to Cursor”命令,使程序运行到指定行时暂停。左下方窗口中列出此时各变量的取值,如图1-28所示。

图1-28 各变量的取值

s=8,差d=3(x=3,y=5,差d=3肯定是错误的),商q=0,模r=3,最终结果res=26,仔细查看程序中负责计算差d的的语句发现将“x-y”误写成“s-y”。找到了错误,此时可以通过选择“调试”→“Stop Debugging”命令中断当前的调试过程。然后修改所发现的错误后,再一次执行程序将得出正确结果。

图1-27中显示的变量是“自动查看”方式,即VC6.0自动显示当前运行过程中的变量值。如果变量比较多,显示的窗口比较混乱,为此可以在“Watch”列表中添加需要监控的变量名。

设置临时断点的调试手法使用方便,也常配合单步执行的方法仔细检查程序执行每一步后各变量取值的动态变化情况。如先通过“Run to Cursor”执行到某一个光标临时断点行,而后通过使用“Step Ove”或“Step Into”命令开始单步执行。每执行一步后都要仔细观察并分析系统自动给出的各变量取值的动态变化情况,以及时发现异常而找到出错原因。

4.设计合适的程序调试方案

针对不同的程序,都需要在分析其执行结果及其程序结构的基础上来设计相应的调试方案。宗旨是想方设法逐步缩小问题的范围,直到最后找到出错位置。

如果一个程序除main外,还有一个自定义函数f。若已经确认调用该函数前计算出的res值(或sdq、r或其中之一的结果值)不正确,则可如同上一程序那样在计算出res变量值的下一行(或在靠前一些的某一行)设置断点,查看到达该断点后是否一切正常。若不正常,则已经出现错误,从而找到错误或者缩小了范围;若正常,则可断言错误出现在后面。此时可以一次在更靠后一些的适当位置设置新断点,再一次选择“调试”→“Run to Cursor”命令继续检查,也可以通过单步执行在重点怀疑处仔细地逐行检查。

“Step Over”命令不会进入f函数内部执行,若怀疑f函数有问题,则选择“调试”→“Step Into”命令进入f函数内部细致调试。若发现不再需要继续单步调试,则通过“Step Out”命令从函数内部返回到调用语句的下一语句处。

可以看出,程序调试是一件很费时费力而又非常细致的工作。需要耐心,要通过不断的实践来总结与积累调试经验。

作为练习,请读者利用如下程序对上述的调试方法与手段进行多方面的灵活使用与体验:

前面也提到过,通过“Run to Cursor”命令设置并到达的断点是一个临时性断点。设置固定性断点最简单的方法是右键单击在某一程序行,在弹出的快捷菜单中选择“Insert/Remove Breakpoint”命令。

清除固定性断点的方法为用右键单击具有圆形黑点标志的固定断点行,在弹出的快捷菜单中选择“Remove Breakpoint”命令。

设置固定性断点后,通常通过选择“组建”→“开始调试”→“Go”或“调试”→“Go””命令使程序开始执行,直到遇到某断点或遇到程序结束而停止。

还要说明的是,可以随时设置任意多个固定性断点,也可以随时清除它们。选择“编辑”→“Breakpoint”命令,打开一个对话框。在其中的“Break at”文本框中键入要设置断点的程序行的行数(通常是先通过光标选定某一程序行,利用菜单命令打开上述对话框。而后通过在“Break at”文本框右边的小三角按钮并选定系统提供的程序行的行数),然后单击“OK”按钮;如果要清除某断点,可在“Breakpoints”列表栏中选定它,之后单击“Remove”按钮。实际上,除位置断点外,通过选择“编辑”→“Breakpoint”命令还可以设置数据断点,消息断点,以及条件断点等。

调试常用的快捷键如表1-1所示。

表1-1 调试常用的快捷键

1.2.5 常见的错误提示

以下是一些常见的编译和组建期间的程序出错英文提示及相应的中文意思,供参考。

1.常见编译错误

(1)error C2001:newline in constant

编号:C2001。

直译:在常量中出现了换行。

错误分析:

· 字符串常量、字符常量中是否有换行。

· 在该语句中,某个字符串常量的尾部是否漏掉了双引号。

· 在该语句中,某个字符常量中是否出现了双引号字符,但是没有使用转义符‘\’。

· 在该语句中,某个字符常量的尾部是否漏掉了单引号。

· 是否在某句语句的尾部或语句的中间误输入了一个单引号或双引号。

(2)error C2015:too many characters in constant

编号:C2015。

直译:字符常量中的字符过多。

错误分析:单引号表示字符型常量,一般其中必须有且只能有一个字符(使用转义符时,转义符所表示的字符作为一个字符处理)。如果单引号中的字符数多于4个,则引发这个错误;另外,如果语句中某个字符常量缺少右边的单引号,也会引发这个错误,如:

值得注意的是如果单引号中的字符数是2~4个,编译不报错,输出结果是这几个字母的ASCII码作为一个整数(int,4B)整体处理的数字。

(3)error C2137:empty character constant

编号:C2137

直译:空的字符定义。

错误分析:原因是连用了两个单引号,而中间没有任何字符,这是不允许的。

(4)error C2018:unknown character'0x##'

编号:C2018。

直译:未知字符0x##。

错误分析:0x##是字符ASCII码的16进制表示法,这里说的未知字符通常是指全角符号、字母、数字,或者直接输入了汉字。如果全角字符和汉字用双引号包含,成为字符串常量的一部分,则不会引发这个错误。

(5)error C2041:illegal digit'#'for base'8'

编号:C2141。

直译:在八进制中出现了非法的数字‘#’(这个数字#通常是8或者9)。

错误分析:如果某个数字常量以0开头(单纯的数字0除外),那么编译器会认为这是一个8进制数字。例如,“089”“078”“093”都是非法的,而“071”是合法的,等同于十进制中的57。

(6)error C2065:'xxxx':undeclared identifier

编号:C2065。

直译:标识符“xxxx”未定义。

错误分析:标识符是程序中出现的除关键字之外的词,通常由字母、数字和下画线组成。不能以数字开头,也不能与关键字重复,并且区分大小写。变量名、函数名、类名、常量名等都是标识符,所有的标识符都必须先定义,后使用。标识符有很多种用途,所以错误也有多种原因。

· 如果“xxxx”是一个变量名,那么通常是程序员忘记了定义这个变量,或者拼写错误、大小写错误所引起的,所以首先检查变量名是否正确(关联变量、变量定义)。

· 如果“xxxx”是一个函数名,则怀疑函数名是否没有定义。可能是拼写错误或大小写错误,也有可能是调用的函数不存在;另一种可能是写的函数在调用所在的函数之后,而没有在调用之前声明函数原型(关联函数声明与定义、函数原型)。

· 如果“xxxx”是一个库函数的函数名,如“sqrt”“fabs”,那么检查在cpp文件开始是否包含了这些库函数所在的头文件(.h文件)。例如,使用“sqrt”函数需要头文件math.h。如果“xxxx”就是“cin”或“cout”,那么一般是没有包含“iostream.h”(关联#include、cin、cout)。

· 如果“xxxx”是一个类名,那么表示这个类没有定义,可能性依然是没有定义这个类,或者拼写错误,或者大小写错误,或者缺少头文件,或者类的使用在声明之前(关联类、类定义)。

· 标识符遵循先声明后使用原则,所以无论是变量、函数名、类名都必须先定义,后使用。如使用在前,声明在后,就会引发这个错误。

· C的作用域也会成为引发这个错误的陷阱,在花括号之内的变量不能在这个花括号之外使用,类、函数、if、do(while)、for所引起的花括号都遵循这个规则(关联作用域)。

· 前面某句语句的错误也可能导致编译器误认为该语句有错,如果前面的变量定义语句有错误,编译器在后面的编译中会认为该变量从来没有定义过,以致后面所有使用这个变量的语句都报这个错误。如果函数声明语句有错误,那么将会引发同样的问题。

(7)error C2086:'xxxx':redefinition

编号:C2374。

直译:“xxxx”重复声明。

错误分析:变量“xxxx”在同一作用域中定义了多次,检查“xxxx”的每一次定义,只保留一个或者更改变量名。

(8)error C2374:'xxxx':redefinition;multiple initialization

编号:C2374。

直译:“xxxx”重复声明,多次初始化。

错误分析:变量“xxxx”在同一作用域中定义了多次,并且进行了多次初始化。检查“xxxx”的每一次定义,只保留一个或者更改变量名。

(9)C2143:syntax error :missing';'before (identifier)'xxxx'

编号:C2143。

直译:在标识符 “xxxx”前缺少分号。

错误分析:这是VC6.0编译期间最常见的误报,当出现这个错误时往往所指的语句并没有错误,而是它的上一个语句发生了错误。合适的做法是编译器报告在上一个语句的尾部缺少分号,上一个语句的如下几种错误都会导致编译器报出这个错误。

· 语句的末尾真的缺少分号,补上即可。

· 语句不完整,或者有明显的语法错误,或者根本不能作为一个语句(有时是无意中按键所致)。

· 如果发现发生错误的语句是cpp文件的第1行语句,在本文件中检查没有错误。但其使用双引号包含了某个头文件,那么检查这个头文件,在这个头文件的尾部可能有错误。

(10)error C4716:'xxx':must return a value

编号:C4716。

直译:“xxx”必须返回一个值。

错误分析:函数声明了有返回值(不为void),但函数实现中忘记了return返回值。如果函数确实没有返回值,则修改其返回值类型为void;否则在函数结束前返回合适的值。

(11)warning C4508:'main':function should return a value;'void'return type assumed

编号:C4508。

直译:main函数应该返回一个值,void返回值类型被假定。

错误分析:

· 函数应该有返回值,声明函数时应指定返回值的类型,确实无返回值的应将函数返回值声明为void。若未声明函数返回值的类型,则系统默认为整型int。此处的错误估计是在main函数中没有return返回值语句,而main函数要么没有声明其返回值的类型,要么已经声明。

· warning类型的错误为警告性质的错误,并不一定有错。程序仍可以被成功编译和组建,但可能有问题和风险。

(12)warning C4700:local variable'xxx'used without having been initialized

编号:C4700。

直译:警告局部变量“xxx”在使用前没有被初始化。

错误分析:这是初学者常见的错误,如以下程序段就会产生这样的警告。而且程序的确有问题,应加以修改,尽管编译、组建可以成功。若不修改,x值无法确定,是随机的,判断其是否与3相同没有意义。可能在调试时结果正确,但更换计算机后运行则错误,。

2.常见组建错误

(1)error LNK2001:unresolved external symbol _main

编号:LNK2001。

直译:未解决的外部符号_main。

错误分析:缺少main函数,查看main的拼写或大小写是否正确。

(2)error LNK2005:_main already defined in xxxx.obj

编号:LNK2005。

直译:_main已经存在于xxxx.obj中。

错误分析:直接的原因是该程序中有多个main函数,这是初学C++的低年级同学在初次编程时经常犯的错误。这个错误通常不是在同一个文件中包含多个main函数,而是在一个工程中包含了多个cpp文件,而每个cpp文件中都有一个main函数。引发这个错误的过程一般是在创建第2个C文件时没有关闭原来的项目,所以无意中新的C文件添加到上一个程序所在的项目中。切换到“File View”视图,展开“Source Files”节点后会发现有两个文件。

在编写C程序时,一定要理解工作空间和工程。每一个程序都是一个工程,一个工程可以编译为一个应用程序(*.exe)或者一个动态链接库(*.dll)。通常每个工程下面可以包含多个.c文件、.h文件,以及其他资源文件。在这些文件中只能有一个main函数。

当完成一个程序后编写另一个程序之前,一定要选择“文件”→“关闭工作空间”命令,完全关闭前一个项目。避免这个错误的另一个方法是每次写完一个C程序后关闭VC6.0,然后重写打开编写下一个程序。