购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.5 第一个C++程序

C++程序的开发通常要经过编辑、编译、链接、运行调试这几个步骤。编辑是将源程序输入到计算机中,产生扩展名为.cpp的物理文件。编译是将程序的源代码转换为机器语言代码。但是编译后的程序还不能由计算机执行,还需要进行链接操作。链接是将多个目标文件以及库中的某些文件连在一起,生成一个扩展名为.exe的可执行文件。最后还要对可执行文件进行运行、调试。

1.5.1 C++程序的结构

每个C++程序都由一个或多个文件组成。根据约定,用于存储源代码的文件有两类:头文件和源文件。

□头文件:它可以包含描述程序所需的数据类型的代码,以及其他类型的声明。这些文件之所以称为头文件,是因为通常在其他源文件的开头包含它们。头文件通常用文件扩展名.h来区分,但这不是强制的,在一些系统中,也使用其他扩展名来标识头文件,例如.hpp。

□源文件:它的扩展名是.cpp,包含了函数声明,即程序的可执行代码。这些代码通常引用在自己的头文件中定义的数据类型的声明或定义。编译器在编译代码时,需要知道这些声明或定义,因此应在文件的开头通过#include指令指定.cpp文件中需要的.h文件。#include指令是编译器的一个预编译指令,它可以把指定头文件的内容插入代码。还要为代码需要的标准库头文件添加#include指令。

图1.12中展示了C++程序的一般结构。

从图1.13中可以看到,一个完整的程序还可能包含一些资源文件,例如字符串资源、控件资源等。

图1.13说明:程序中的源代码包含在两个.cpp文件和一个头文件中。其中的“StdAfx.cpp”与“StdAfx.h”是对应的一组源文件,按照约定的规则,一组对应关联的文件的名字是一样的(这种约定不是必须的)。

图 1.12 C++程序结构

图 1.13 C++程序文件

下面从一个最简单的程序入手,看一个C++程序的组成结构(源代码\Chapter 1\1_1)。【实例1.1】简单的Hello World入门实例。

helloworld.cpp

#include"stdafx.h"  //包含预编译头文件

#include<iostream>  //包含系统库

using namespace std;  //使用命名空间std

int main(int argc, char*argv[])

{

cout<<"Hello World!"<<endl;  //输出字符串信息

return 0;

}

【输出结果】实例1.1的输出结果如下所示。

Hello World!

【代码剖析】

对上面C++程序的详细说明如下:

1)注释。程序中类似“//包含预编译头文件”这样的代码属于注释代码,它不属于可执行代码,不会被编译到最后的目标程序。所有以两个斜线符号“//”开始的程序行都被认为是注释行,这些注释行是程序员写在程序源代码内,用来对程序做简单解释或描述的,对程序本身的运行不会产生影响。在本例中,这行注释对本程序是什么做了一个简要的描述。良好的代码注释有利于增强代码的可读性,同时增加代码的可维护性,大大降低软件的维护成本。

2)预编译头文件。程序中使用#include指令包含了一个头文件“stdafx.h”,这个头文件被称为“预编译头文件”。预编译头物理上与通常的.obj文件是一样的,但编译进预编译头的.h、.c、.cpp文件在整个编译过程中只编译一次,例如预编译头所涉及的部分不发生改变的,在随后的编译过程中此部分不重新进行编译,进而大大提高编译速度,并便于对头文件进行管理,也有助于杜绝重复包含问题。程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。用于创建预编译头文件的第一次编译所花费的时间比后面的编译稍长一些。通过包含预编译代码可以加快后面的编译速度。

C++程序都可以预编译。在C++编程中,常见的做法是将类接口信息分别放到不同的头文件中。此后就可以将这些头文件包含在使用该类的程序中。通过预编译这些头文件,可以缩短程序的编译时间。

注意 尽管每个源文件只能使用一个预编译的头文件(.pch),但在一个项目中可以使用多个.pch文件。

3)包含需要使用到的标准库文件。以#标志开始的句子是预处理器的指示语句。它们不是可执行代码,只是对编译器作出指示。在本例中“#include<iostream>”告诉编译器的预处理器将输入/输出流的标准头文件(iostream)包括在本程序中。这个头文件包括了C++中定义的基本标准输入/输出程序库的声明。此处它被包括进来是因为在本程序的后面部分将用到它的功能。

4)声明需要使用的命名空间。C++标准函数库的所有元素都被声明在一个命名空间中,这就是std名空间。因此为了能够访问它的功能,用语句“using namespace std;”来表达将使用标准名空间中定义的元素。这条语句在使用标准函数库的C++程序中频繁出现,本书的大部分代码例子中也将用到它。

5)主函数。主函数也叫一个程序的入口函数,是所有程序运行的入口。C++程序至少包含一个函数“main()”,但程序一般还包含许多其他函数,一些是程序员自己编写的,另外一些是标准库函数。程序的函数存储在许多源文件中,其文件的扩展名通常是.cpp,其他扩展名.cxx和.cc也较常见。

main()函数在被操作系统调用时开始执行,程序中的其他函数由main()或其他函数调用。执行一个函数就称为调用函数。在调用函数时,可以给它传送数据项。要传送给函数的数据项放在调用操作中函数名后面的括号中。在函数执行完后,执行控制就返回到调用函数的地方。

事实上,main()函数既可以是无参函数,也可以是有参函数。对于有参的形式来说,就需要向其传递参数。但是其他任何函数均不能调用main()函数。同样也无法向main()函数传递参数,只能由程序之外传递而来。main()函数的完整形式如下:

int main(int argc, char*argv[])

{

……

}

从函数参数的形式上看,包含一个整型和一个指针数组。当一个C++的源程序经过编译、链接后,会生成扩展名为.EXE的可执行文件,这是可以在操作系统下直接运行的文件,换句话说,就是由系统来启动运行的。对main()函数既然不能由其他函数调用和传递参数,就只能由系统在启动运行时传递参数了。在操作系统环境下,一条完整的运行命令应包括两部分:命令与相应的参数。其格式为:

命令参数1参数2……参数n

此格式也称为命令行。命令行中的命令就是可执行文件的文件名,其后所跟参数需用空格分隔,并作为对命令的进一步补充,也就是传递给main()函数的参数。

命令行与main()函数的参数存在如下的关系,设命令行为:

program str1 str2 str3 str4 str5

其中program为可执行文件名,也就是一个由program.c经编译、链接后生成的可执行文件program.exe,其后添加了5个参数。对main()函数来说,它的参数argc记录了命令行中命令与参数的个数,共6个。指针数组的大小由参数argc的值决定,即为char*argv[6],数组的各指针分别指向一个字符串。应当引起注意的是接收到的指针数组的各指针是从命令行开始接收的,首先接收到的是命令,其后才是参数。

数组的对应关系如下:

argv[0]----program.exe

argv[1]----str1

argv[2]----str2

argv[3]----str3

argv[4]----str4

argv[5]----str5

6)打印输出。“cout<<"Hello World!"<<endl;”语句在本程序中最重要。cout是C++中的标准输出流(通常为控制台,即屏幕),这句话把一串字符串(本例中为“Hello World”)插入输出流(控制台输出)中。cout的声明在头文件iostream中,所以要想使用cout必须将该头文件包括在程序开始处。

注意 这个句子以分号结尾。分号表示了一个语句的结束,C++的每一个语句都必须以分号结尾(C++程序员最常犯的错误之一就是忘记在语句末尾写上分号)。

7)返回值。返回语句(return)引起主函数main()执行结束,并将该语句后面所跟代码(在本例中为0)返回。这是在程序执行没有出现任何错误的情况下最常见的程序结束方式。

1.5.2 创建源程序

下面介绍使用VC++6.0创建一个控制台源程序工程的一般步骤。

启动VC++6.0,单击Projects选项卡,在列表框中选择Win32 Console Application,在Project name文本框中输入工程名helloworld,然后在Location文本框中输入该工程名所在的文件夹,如图1.14所示,单击OK按钮。在Win32 Console Application-Step 1 of 1对话框中选择A simple application选项,如图1.15所示,最后单击Finish按钮。创建一个简单控制台的工程完成。

图 1.14 选择工程类型和设置工程名以及工程路径

图 1.15 控制台类型选择

工程创建完毕后,就可以看到“helloworld.cpp”源文件,编程者便可以在该.cpp文件的main()函数中添加功能代码。

1.5.3 编译、链接和运行程序

从C++源代码中创建可以执行的程序模块需要两步。第一步是编译器把每个.cpp文件转换为对象文件,其中包含了与源文件内容对应的机器码。第二步是链接程序把编译器生成的对象文件合并到包含完整可执行程序的文件中。

实际上,编译是一个迭代的过程,因为在源代码中总是会有输入错误或其他错误。更正了每个源文件中的错误后,就可以进入链接步骤,但在这一步可能会发现更多的错误。即使链接步骤生成了可执行模块,程序仍有可能包含逻辑错误,即程序没有生成希望的结果。为了更正这些错误,必须回过头来修改源代码,再编译。这个过程会继续下去,直到程序按照希望的方式执行为止。如果程序的执行结果不像我们宣称的那样,其他人就有可能找到程序编写者本应发现的许多错误,这是毋庸置疑的。一般说来,如果程序非常大,就总是包含错误。

下面详细讨论一下这两个基本步骤(编译和链接):

1)编译。源文件的编译过程包含两个主要阶段,第一个阶段是预处理阶段,在正式的编译阶段之前进行。预处理阶段将根据已放置在文件中的预处理指令来修改源文件的内容。#include指令就是一个预处理指令,它把头文件的内容添加到.cpp文件中。还有其他许多预处理指令。

通常,一个环境需要的代码跟另一个环境所需的代码可能有所不同,因为可用的硬件或操作系统是不同的。在许多情况下,可以把用于不同环境的代码放在同一个文件中,再在预处理阶段修改代码,使之适应当前的环境。

2)链接。编译器为给定源文件输出的是机器码,执行这个过程需要较长时间。对应于某个源文件的对象文件包含在其他源文件中定义的函数引用或其他指定项的引用上,而这些函数或项仍没有被解析。同样,也没有建立同库函数的链接。实际上,这些函数的代码并不是文件的一部分。这些工作是由链接程序(有时称为链接编辑器)完成的。

链接程序把所有对象文件中的机器码组合在一起,并解析它们之间的交叉引用。它还集成了对象模块所使用的库函数的代码。这是链接程序的一种简化表示,因为这里假定在可执行模块中,模块之间的所有链接都是静态建立的。实际上有些链接是动态的,即这些链接是在程序执行时建立的。

链接程序静态地建立函数之间的链接,即在程序执行之前建立组成程序的源文件中所包含的函数链接。动态建立的函数之间的链接(在程序执行过程中建立的链接)将函数编译并链接起来,创建另一种可执行模块(动态链接库或共享库)。动态链接库中的函数链接是在程序调用函数时才建立的,在程序调用之前,该链接是不存在的。

动态链接库有几个重要的优点。一个主要的优点是动态链接库中的函数可以在几个并行执行的程序之间共享,在执行的多个函数需要动态链接库中的函数所提供的服务时,这将节省同一个函数占用的内存空间。另一个优点是动态链接库在调用其中的函数之前是不会加载到内存中的。也就是说,如果不使用给定动态链接库中的函数,该动态链接库就不会占用内存空间。动态链接库是与操作系统紧密相关的一个系统功能。 LvNl/pzBMP/79ZvizAf4mcrSdsf4c/yxx0j0E1aPlz1sR/ESfqoZQ0K9qK1nJRp5

点击中间区域
呼出菜单
上一章
目录
下一章
×