1.5 第一个C语言程序
经过前面一系列的不懈努力,终于可以编写第一个C语言程序了,是不是有点激动?嘿嘿,作为有着丰富经验的我(有点倚老卖老啊),未雨绸缪、高瞻远瞩,首先想到了本书中会有很多的代码,为了便于对这些代码进行管理,我决定采用三级目录管理方式:首先在D盘下建立名称为“大话C语言代码”的总文件夹;然后在总文件夹下面为每一章建立一个对应名称的章文件夹;再接着在各个章文件夹中建立具体的项目文件夹。例如,现在要编写第一个C语言程序,我就在“D:\第一章\”下面建立了一个名称为“第一个C语言程序”的项目文件夹。然后进入该项目文件夹,并以此为工作目录,新建一个文本文件“first.c”,注意文件名后缀为.c,表明该文件为C语言的源文件(C标准的规定)。文件建好后,右击该文件,在弹出的快捷菜单中选择“Edit with Notepad++”命令,使用Notepad++来打开文件。现在就可以在文件里编写代码了,具体内容如下。
代码输入完成后保存,然后对这个源文件进行编译。可以直接在当前工作目录的路径栏里输入“CMD”并按下Enter键,这样就可以打开控制台窗口,并以当前目录作为工作目录,如图1.17所示。
图1.17 在当前目录启动控制台窗口
在控制台窗口中输入编译命令“gcc first.c”,就会使用GCC编译器来对“first.c”这个源文件进行编译,最终生成一个名为“a.exe”的可执行文件。如果想得到一个指定名字的可执行文件,如“first.exe”,则把之前的编译命令改为“gcc first.c -o first.exe”即可,也就是在编译命令中加入“-o”选项来指定输出文件的名字。
最终生成的这个可执行文件,可以直接用鼠标双击执行,也可以在控制台窗口中执行。不过直接执行的话,可能会出现窗口一闪而过的景象,导致用户看不到运行的结果。但这并不是程序的问题,而是现在的计算机执行速度太快。当程序执行完毕后,系统会自动关闭窗口。看不到运行结果怎么办?有很多解决办法,最简单的一种就是让程序在控制台窗口中运行,这样即使程序执行完毕,这个控制台窗口也不会消失,用户就有足够的时间来观察程序运行的结果,控制台窗口如图1.18所示。
如果在窗口中看到的是一串乱码,别担心,这只是因为代码字符和控制台窗口的字符编码不一致造成的,只要重新进行字符编码就可以了。具体步骤为用Notepad++打开源文件“first.c”,选择“编码”菜单,执行“转为ANSI编码”菜单项,再保存文件,重新编译即可。这时再重新运行程序,就不会仍然是一串乱码。
图1.18 编译、执行和查看运行结果
是不是有点成就感了?经过输入简单的几行代码,就能通过编译器生成一个可执行文件,通过执行这个可执行文件,就会得到一个程序的运行结果。但为什么要输入这些代码?这些代码到底是什么意思?我能随便改里面的代码吗?编译器是怎么编译的?下面就来解释一下这些代码的作用和意义。
1.5.1 C语言的代码注释
想必大家在学习时有过这样的经历:学习文言文时,遇到看不到的句子,就在旁边加上白话文来说明,学习英文时,遇到不会读的单词就用音近的中文文字来标注,学习课文时,每一段都会写出段落大意,并能说出整篇文章的中心思想。
类似地,C语言的代码注释的作用也是如此。A同学写出的程序代码,B同学拿过来可能就看不懂,甚至时间久了,A同学自己也理解不了。“好记性不如烂笔头”,如果在代码里加上恰当的注释,对于代码交流就相对容易多了,而且即使过了很长的时间,看到注释也自然能回想到当初为何这么写代码。所以,代码注释就是为了更好地理解代码,方便相互交流的一种文字说明。C语言中的代码注释方式有两种:块注释和行注释。
1.块注释
“第一个C程序”中的首行就是一个块注释,它是用“/*”和“*/”所包含的一段(一行或多行)文字。块注释非常灵活,可以出现在程序代码的任何部分,甚至在某一条语句之中。但块注释不允许被嵌套使用,即不能在一个块注释中又出现另一个块注释。例如“/*aa/*bb*/cc*/”,块注释是从“/*”开始,到遇见第一个“*/”结束,所以真正的块注释部分只有“/*aa/*bb*/”,后面的“cc*/”就不是块注释的部分了。
2.行注释
C语言程序代码中,以“//”开头的部分称为行注释。顾名思义,行注释只能注释一行,从“//”开始,直到该行的末尾,都为注释的部分。如果要使用行注释来注释多行,就得在每一行的行首都使用“//”。
最后,还需要强调两点:①注释只是给人看的,并不会参与编译,也就是说在真正编译的时候这些注释都会被忽略掉。②错误的注释比没有注释更糟糕,注释的内容要做到表述清晰、文词达意,若代码被修改,注释也要及时更新。
1.5.2 文件包含
“第一个C程序”中的第二行是一条预处理语句,关于预处理我们会在后面的章节中学习,现在只要简单理解一下该条语句的功能就可以。“#include <stdio.h>”的作用是将一个名字叫“stdio.h”的标准库头文件包含到当前源文件中。为什么要这样做?现在只能说,因为在后面要用到一个标准库函数printf,也就是说,如果在程序中需要用到标准库里的函数,就需要包含相应的标准库头文件,而且在后面的程序案例中大多都会用上这个“stdio.h”头文件,因为在这个头文件中,除了printf外,还声明了许多其他的标准输入、输出、文件等相关的函数。同样地,函数也将在后面的章节中来详细学习,因为对于初学者,如果一时接纳的新知识太多,而又不可能在短时间内把这些知识都理解通透,就很容易让自己迷茫,这样的学习效果并不好,会失去对学习的热情和信心。分清主次、循序渐进,以这样的方式和心态来学习,才能成功。
1.5.3 main函数
虽然把函数放到了后面的章节中来学习,但现在依然不可避免地撞上了它。还是老办法,先简单地认识一下就行。函数是为满足某些特殊功能而构建的子程序,例如上面的例子里所使用的printf函数“printf("第一个C程序!\n");”,它的功能就是把字符串“第一个C程序!\n”输出到屏幕上。在C语言中用两个“"”包含起来的文字称为字符串,而字符串中最后的那个“\n”的作用就是换行,也就是在输出“第一个C程序!”后进行一个换行的操作。当然printf函数的功能不仅如此,我们会在后面详细学习。现在先来看一下main函数,上面的例子中,除了第一行的注释和第二行的文件包含,剩下的就是main函数了,我们更习惯称它为主函数或者入口函数。因为C语言规定,在C程序代码中,必须有一个main函数。也就是C程序中可以没有其他的函数,但必须要有main函数(又称为主函数)。程序一运行,系统就会到程序中查找main函数,然后开始执行它,也就是程序总是从main函数开始执行的,所以又称之为入口函数。
在main函数中(位于一对大括号之间)有两行代码:第一行调用printf函数来输出字符串,第二行使用return语句返回一个0值。C语言规定,main函数在执行完毕需要返回一个整数值,用这个值可以来检查程序的退出状态。若返回的是0值,表示程序执行成功正常退出,返回非0值表示程序是非正常退出的。
1.5.4 C程序编译流程
由上面的例子可知,只要事先写好一个“.c”后缀的源文件,然后使用“gcc命令”进行编译,就可以得到一个“.exe”后缀的可执行的C程序文件。但这期间并非只有一道工序,而是分别经过了预处理、编译、汇编和链接四个流程,如图1.19所示。
图1.19 C程序的编译流程
1.预处理
在源文件被编译之前,首先要进行预处理的工作,也就是对源代码进行相应的展开、替换和清理。在本例中,预处理工作有两项:①把代码注释部分去掉,不让其参与编译;②把“stdio.h”文件包含进来,即用“stdio.h”中的内容替换在“#include <stdio.h>”位置。
2.编译
源文件被预处理之后,再以字符流的形式进行处理,进行词法和语法的分析,然后通过汇编器将源代码指令转变成汇编指令,生成相应的汇编文件。
3.汇编
汇编是指把汇编语言代码翻译成目标机器指令的过程,也就是把汇编码转换成机器所能识别的二进制码,通常把经过汇编之后生成的文件称为目标文件。
4.链接
经过汇编之后生成的目标文件并不能立即被执行,还需要由链接器将代码在执行过程中用到的其他目标代码及库文件进行链接,最终生成一个可执行程序。例如本例中用到了printf函数,就需要找到包含该函数的标准库文件,对它进行链接。
1.5.5 C语言调试
大家有没有想过,在以后编写程序代码时可能会出现这样那样的错误,这是肯定的,虽然像是给大家泼冷水,但这是不可回避的问题。不可回避就勇敢接受吧。
从错误出现的时间上可分为两类:编译时错误和运行时错误。所谓编译时错误就是在写好源文件后,在进行编译时出现的错误,主要分为语法错误和链接错误两种。语法错误比较明显,解决也相对简单,编译器通常会给出错误的代码位置,程序员根据提示位置到代码中进行修改就行了。而链接错误通常是由于所需的库文件找不到或函数没有具体的实现等原因造成的。另一种是运行时错误,也就是编译可以顺利通过,但程序运行时出现了错误。例如程序崩溃和异常退出,程序运行造成死机,程序运行的结果不正确等。这类错误处理起来相对棘手,通常需要对程序进行反复的调试,才能找到问题所在。所以程序的调试主要是针对运行时错误而言的。那下面就来看看有哪些调试方式吧。
1.人工调试
人工调试就是通过人的眼看、脑算、手记等方法对程序代码进行调试的过程。通常采取逐行跟踪追查的办法,用人脑来推算、模拟出程序的运行轨迹,直至找出问题位置。这种调试方式适合代码量少、结构简单的程序。
2.代码调试
代码调试通常采取在程序不同位置设置一些特定条件或可输出信息的程序代码,然后根据特定条件的执行情况或输出信息来判断、寻找错误的大概位置。这种调试方式适合于代码量大、结构复杂的程序代码,但同时要求调试者有较强的逻辑判断能力。
3.工具调试
工具调试采用功能强大的调试工具来对程序进行跟踪调试,这些调试工具通常都具有代码跟踪、断点设置、内存查询、变量监视等功能,可以通过一步一步地执行代码来观察程序的运行状态,极大地方便了对程序的调试。
其实在1.4节安装的MinGW里包含了一套工具。除了GCC编译器外,还包含一个名为GDB的调试工具,使用GDB可以很方便地配合GCC进行程序的调试。不过想要使用GDB来调试程序,在使用GCC编译源文件时需要多加一个选项“-g”,即完整的编译命令为“gcc -g first.c -o first.exe”。“-g”选项的作用是让编译后的程序里包含相关的调试信息,这样才能让GDB调试工具发挥作用。
在控制台窗口输入命令“gdb first.exe”,这样就进入程序调试界面了,如图1.20所示。
图1.20 使用GDB调试工具
现在就可以在“〈gdb〉”提示符后面输入相应的调试命令来对程序进行调试了。例如“list”或其简写“l”可以显示当前的源代码,也可以用“list 1,5”或“l 1,5”来显示源代码中的1~5行;使用“break 3”或“b 3”在第3行代码处设置断点,然后使用“run”或“r”来运行程序,程序将启动并暂停在断点处,然后不断使用“step”或“s”来一步一步地执行程序,还可使用“continue”让程序运行到下一个断点处;使用“quit”或“q”可退出调试。一些常用的调试命令如表1.1所示。
表1.1 gdb调试命令