2. #include指令—— 头文件
在注释之后有一个#include指令:
#include
之所以称其为指令,是因为它命令编译器完成某项任务—— 此处是在编译之前,在此程序的源文件中插入文件iostream的内容,该文件名位于尖括号之间。iostream文件称为头文件,因为它总是插入到另一个源文件中。iostream头文件是标准C++库的一部分,它包含一些使用输入和输出语句所需的定义。如果没有在此程序中包括iostream的内容,那么不能编译这个程序,因为在这个程序中使用的输出语句依赖该文件中的一些定义。Visual C++提供了许多不同的标准头文件,它们具有各种各样的功能。在进一步学习语言工具时,将看到更多的头文件。
由#include指令插入的文件的名称不一定写在尖括号之间。头文件名也可以写在双引号中。因此上面的代码也可以写成:
#include "iostream"
两者之间的唯一区别是编译器将在什么地方查找此文件。如果头文件名是用双引号引起来的,则编译器先在包含此指令的源文件所在的目录中搜索头文件。如果头文件未找到,编译器再搜索存储标准头文件的目录。
如果文件名是用尖括号括起来的,则编译器只搜索包含标准头文件的目录。因此,想在源文件中包含标准头文件时,应该将文件名用尖括号括起来,因为这样的搜索速度更快。而要包含其他头文件,一般是自己创建的头文件,则应该将文件名用双引号引起来;否则,根本找不到。
#include语句是几个预处理器指令中的一个,本书后面将介绍其他预处理指令。编辑器会在编辑窗口中用蓝色突出显示它们。预处理器指令是由编译的预处理阶段执行的命令,这个阶段在代码编译成目标代码之前执行,在编译源代码之前,预处理器指令通常以某种方式作用于它们。预处理器指令都以#字符开头。
3. 名称空间和using声明
如第1章所述,标准库是一个大型的例程集合,用于执行许多常见的任务,如处理输入和输出,以及执行基本的数学计算。由于标准库中的这些例程以及其他具有名称的事物数量巨大,因此用户使用的名称可能无意中与标准库中的名称雷同。名称空间是一种机制,它可以将无意中使用重名的风险降至最低,其方法是将一组给定的名称(如标准库中的名称)与一种姓(family name)关联起来,这种姓就是名称空间名称。
在名称空间的代码中定义的每个名称都有一个关联的名称空间名称。标准库工具定义在std名称空间内,所以标准库中的每一项都有自己的名称,以及作为限定符的名称空间名称std。标准库中cout和endl的全名是std::cout和std::endl,第1章介绍过这些名称。将名称空间名称和实体名分隔开的两个冒号构成了称为“作用域解析运算符”的运算符,本书后面将介绍这种运算符的其他用途。在程序中使用全名会使代码看起来有点混乱,所以最好使用不由名称空间名称std限定的简化名。在前面的程序中,iostream的#include指令后面的两行代码使之得以实现:
using std::cout;
using std::endl;
这些是using声明,它们告诉编译器,要在不指定名称空间名称的情况下使用名称空间std中的名称cout和endl。编译器假定,在第一个using声明之后,只要使用名称cout,就表示std::cout。名称cout表示对应于命令行的标准输出流,名称endl表示换行符,并刷新输出缓存。本章后面会详细介绍名称空间,包括如何自定义名称空间。
2.1.1 main()函数
上述示例中的函数main()包括将它定义为main()的函数头,以及从第一个左大括号({)到对应的右大括号(})之间的所有语句。这对括号将这个函数中的可执行语句包围起来,它们总称为函数体。
所有函数都包括一个定义函数名称的头,然后是函数体,它由包括在大括号之间的一些程序语句组成。函数体也可以不包含任何语句,这时它不做任何事情。
不做任何事情的函数似乎有些多余,但是在编写大程序时,一开始可以先勾画出函数中的完整程序结构,而忽略许多函数的代码,使它们有一个空的或者最小的函数体。这意味着,可以随时编译和执行包含所有函数的整个程序,并逐步给函数添加详细的代码。
2.1.2 程序语句
main()函数体的每个程序语句都以一个分号结束。分号表示语句的结束,而不是这一行的结束。因此,为了使代码更易于理解,可以把一个语句扩展成几行,也可以把几个语句放在同一行中。程序语句是定义程序功能的基本单元。这有点像文章中一个段落的句子,每个句子都独立地表达一个行为或想法,和这个段落中的其他句子联系和组合起来,就表达出比较全面的想法。一个语句就是计算机将要执行的一个行为的自包含定义,和其他语句组合起来, 就可以定义比较复杂的行为或计算。
函数的行为始终由一些语句来表达,每个语句都以分号结束。看看刚才编写的示例中的语句,大致了解它是如何运行的。本章后面将讨论每种类型的语句。
在main()函数体中,第一个语句是:
int apples, oranges; // Declare two integer variables
这个语句定义了两个变量apples和oranges。变量是一段已命名的计算机内存,用于存储数据,引入一个或多个变量名称的语句称为变量声明。关键字int表明apples和oranges变量将存储整数值。每当把一个变量的名称引入程序时,都要指定它将存储的数据类型,这称为变量的类型。
接下来的这个语句声明了另一个整型变量fruit:
int fruit; // ...then another one
虽然可以在同一个语句中声明几个变量,如同前面的语句声明apples和oranges那样。但是,一般最好用一个语句声明一个变量,独占一行,这样就可以单独注释,以解释它们的用途。
示例中的下一行代码是:
apples = 5; oranges = 6; // Set initial values
这行代码包含两个语句,每个语句都以一个分号结束。这样做的目的是说明可以把多个语句放在一行中。这不是强制的,但良好的编程习惯一般是一行只编写一个语句,使代码较容易理解。良好的编程习惯是采用使代码易于理解、且使出错的可能性降至最低的编码方法。
这两个语句分别将数值5和6存储到变量apples和oranges中。这些语句称为赋值语句,因为它们把新值赋给变量,=是赋值运算符。
下一个语句是:
fruit = apples + oranges; // Get the total fruit
这也是一个赋值语句,但稍有不同,因为在赋值运算符的右边是一个算术表达式。这个语句把存储在变量apples和oranges中的数值相加,然后在变量fruit中存储结果。
下面的3个语句是:
cout << endl; // Start output on a new line
cout << "Oranges are not the only fruit... " << endl
<< "- and we have " << fruit << " fruits in all.";
cout << endl; // Output a new line character
它们都是输出语句。第一个语句把由endl表示的换行符发送到屏幕的命令行中。在C++中,输入的来源或输出的目的地称为流。名称cout指定“标准的”输出流,运算符<<表明,该运算符右边的内容将发送到输出流cout。<<运算符“指出”数据流动的方向,从这个运算符右边的变量、字符串或表达式到左边的输出目的地。因此,在第一个语句中,由名称endl表示的值(即换行符)将发送到由名称cout标识的流—— 传输到cout的数据会写入命令行。把endl发送到流,也会刷新流缓存,然后把所有输出发送到命令行。
名称cout和运算符<<的含义定义在头文件iostream中,该文件利用#include指令在程序代码中添加。cout是标准库中的名称,所以它在名称空间std内。如果不使用using指令,就不会识别cout,除非使用了其全限定名std::cout。cout定义为表示标准输出流,因此不应当把它用于其他目的,将相同的名称用于不同的事情很可能引起混淆。
第2个输出语句扩展成如下两行:
cout << "Oranges are not the only fruit... " << endl
<< "- and we have " << fruit << " fruits in all.";
如前所述,每个语句都可以扩展成许多行。语句的结束始终用分号表示,而不是一行的结束。编译器将读取连续的行,并把它们组合成一个语句,直至发现定义该语句结束的分号。当然,这也意味着,如果忘记在语句的结尾处放置分号,那么编译器将假定下一行是同一语句的一部分,并将它们连接到一起。这通常会产生编译器无法理解的东西,所以您将得到一个错误消息。
这个语句将文本字符串"Oranges are not the only fruit…"发送到命令行,后跟另一个换行符(endl),然后是另一个文本字符串"- and we have ",接着是存储在变量fruit中的值,最后是另一个文本字符串"fruits in all."。可以按照这种方法把想输出的一系列内容串起来。这个语句从左向右执行,每一项都依次发送到cout。注意,发送到cout的每一项的前面都有自己的<<运算符。
第三个也就是最后一个输出语句把另一个换行符发送到屏幕,这3个语句将生成前述的输出。这个程序中的最后一个语句是:
return 0; // Exit the program
它将终止main()函数的执行,从而停止这个程序的执行。控制权返回到操作系统,返回的代码0告诉操作系统:应用程序成功终止。忽略main()的return语句,程序应能编译并执行。本章的后面将详细地讨论所有这些语句。
程序中的语句按照编写它们的顺序执行,除非一个语句明确改变了这个自然顺序。第3章将讨论改变执行顺序的语句。
2.1.3 空白
空白包含空格、制表符、换行符、换页符和注释的任意序列。空白将语句的各个部分分隔开,从而使编译器能够识别语句中的一个元素(如int)在何处结束,下一个元素在何处开始。否则,将忽略空白,且不会产生任何影响。
以下面的语句为例:
int fruit; // ...then another one
为了使编译器能够区分int和fruit,它们之间至少有一个空白字符,但是如果添加更多的空白字符,它们将被忽略。这一行分号后面的内容全部是空白,所以都被忽略。
另一方面,下面的语句:
fruit = apples + oranges; // Get the total fruit
fruit和=之间,=和apples之间不需要空白字符,但也可以添加一些空白字符。这是因为=不是字母或数字字符,所以编译器可以将它与其周围的内容区分开。类似地,符号+的两边也不需要有空白字符,但是也可以添加一些。
除了在语句元素之间用作分隔符的空白之外,编译器将忽略其他空白(当然,在双引号之间的一串字符中使用的空白除外)。可以添加一些空白,使程序更具可读性,记住,每当出现分号时,语句就结束。
2.1.4 语句块
可以把几个语句括在一对大括号中,这时它们就变成了块或复合语句。函数体就是块的一个示例。可以把这样的复合语句看成单个语句(在第3章讨论判断语句时,将看到这种情况)。只要是可以放单个语句的地方,都可以放置一个用大括号括起来的语句块。因此,块可以置入其他块中。实际上,块可以嵌套(一个块在另一个块内)至任意深度。
2.1.5 自动生成的控制台程序
上一个示例生成了一个没有源文件的空项目,然后添加了源文件。如果允许Application Wizard生成这个项目,如第1章所述,则这个项目将包含几个文件,下面深入分析它们的内容。创建一个新的Win32控制台项目Ex2_01A,这次允许Application Wizard完成这个项目,而不在Application Settings对话框中设置任何选项。这个项目有4个包含代码的文件:Ex2_01A.cpp和stdafx.cpp源文件、stdafx.h头文件以及targetver.h文件,其中targetver.h文件指定能运行应用程序的Windows的最早版本。这表示一个不执行任何任务的有效程序。从主菜单上选择File | Close Solution菜单项,可以关闭打开的项目。在现有项目打开的情况下,可以创建一个新项目,这时将自动关闭旧项目,除非选择在相同的解决方案中添加它。
Ex2_01A.cpp的内容是:
// Ex2_01A.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
这确实和前一个示例不同。其中有一个用于stdafx.h头文件的#include指令,和程序执行的起始函数_tmain(),而不是main()。第5章将介绍函数头中圆括号的内容。
1. 预编译的头文件
Application Wizard生成的stdafx.h头文件是该项目的一部分,观察一下其中的代码,将看到还有3个#include指令,分别用于前面提到的targetver.h头文件,以及标准库头文件stdio.h和tchar.h。stdio.h是用于标准I/O的老式头文件,在C++当前的标准出台之前使用,它的功能和iostream头文件类似,但没有定义相同的名称。这个控制台示例使用iostream符号,所以需要包含它。tchar.h是Microsoft特有的头文件,它定义文本函数。
stdafx.h仅在修改代码时编译,而不是在每次编译程序时重新编译它。编译stdafx.h会得到一个.pch文件(预编译的头文件),只有没有对应的.pch文件,或者.pch文件的时间戳比stdafx.h文件的时间戳早,编译器才重新编译stdafx.h。一些标准库头文件非常大,所以这个功能可以显著减少编译项目所需的时间。如果仅在Ex2_01A.cpp中给iostream包含#include指令,则每次编译程序时,都重新编译它。如果把它放在stdafx.h中,iostream就只编译一次。因此,stdafx.h应包含不常修改的所有头文件的#include指令。这包括项目的标准头文件和很少修改的所有自定义项目头文件。在学习C++时,不会使用出现在stdafx.h中的这两种头文件。
2. Main函数名
如前所述,在编写使用Unicode字符的程序时,Visual C++支持wmain()作为main()的替代函数, wmain()是main()的Microsoft特有定义,不是标准C++的一部分。tchar.h头文件定义了名称_tmain,它一般由main取代,但是如果定义了符号_UNICODE,它就由wmain取代。为了把程序标识为使用Unicode,需要在stdafx.h头文件的开始处添加下列语句:
#define _UNICODE
#define UNICODE
为什么需要两个语句?定义符号_UNICODE,会让Windows头文件假定,Unicode字符是默认的。定义_UNICODE会给C++标准库附带的C例程头文件带来相同的效果。Ex2_01A项目并不需要这么做,因为Character Set项目属性默认设置为使用Unicode字符。前面详细解释了main()函数,C++控制台示例坚持使用普通但成熟的main()函数,它是标准C++函数,因此是可移植性最好的编码方法。
2.2 定义变量
在所有的计算机程序中,一个基本的目标是操作数据,获得结果。这个过程中的一个基本元素是获得一段内存,可以称其为自己的内存,使用一个有意义的名称引用它,并在其中存储一条数据。所指定的每段内存都称为一个变量。
我们知道,每种变量都存储一种特定的数据,在定义了变量后,它可以存储的数据类型就是固定的。储整数的变量,就不能存储小数。变量在某一时刻包含的值由程序中的语句确定,随着程序计算的进展,它的值通常多次改变。
2.2.1 命名变量
变量的名称(其实是C++中所有事物的名称)可以是任意字母和数字的序列,其中下划线_算作字母,其他字符则不允许使用,如果在名称中使用了一些其他字符,编译程序时就会得到一个错误消息。名称必须以字母或下划线开头,通常表明所存储的信息的种类。名称也称为标识符。
在Visual C++中,变量名最长可以有2048个字符,因此给变量命名有相当大的灵活性。如果使用长名称会使程序难以阅读,除非键盘技巧很高,否则长名称很难输入。更严重的问题是,并非所有编译器都支持这么长的名称。事实上,很少需要使用10或15个字符以上的名称。
最好避免使用以下划线开头、且包含大写字母的名称,如_Upper和_Lower,因为它们可能与相同形式的标准库名称发生冲突。由于同样的原因,还应当避免使用以双下划线开头的名称。
下面是一些有效的变量名:
price discount pShape value_ COUNT
five NaCl sodiumChloride tax_rate
涉及两个或多个单词的有意义的名称可以用各种方式构建:第二个单词和以后各单词的首字母大写,或者在各单词之间插入下划线。前面列表里有一些示例。本书的代码中给名称使用各种样式,但最好在一个程序中只使用一种样式。
8_Ball、7Up和6_pack不是合法的名称。Hash!或Mary-Ann也不是合法的名称。最后这个示例是一个很常见的错误,但带有下划线的Mary_Ann是合法的。当然,Mary Ann不是合法的名称,因为变量名不允许有空白。名称republican和Republican是不同的,因为名称是区分大小写的。一个常见的约定是类名以大写字母开头,变量名以小写字母开头,参见第8章。
……
展开