2.2 C++程序分析
在上例中,可能有很多关键字是初学者不太理解的,本节将详细分析该例中用到的关键字。
2.2.1 #include指令及头文件
在上例中,使用了include这个关键字,但是这个关键字起了什么作用呢?下面就来详细介绍include这个关键字。
include是C++的预处理指令,表示包含C/C++标准输入头文件。C++编译系统会根据头文件名把该文件的内容包含进来。包含指令不仅仅限于.h头文件,可以包含任何编译器能识别的C/C++代码文件,包括.c、.hpp、.cpp、.hxx、.cxx等,甚至.txt、.abc等都可以。
提示
C++虽然主要是在C的基础上发展起来的一门新语言,但它不是C的替代品,也不是C的升级,不要用""代替<>来包含系统头文件,虽然有些编译器允许你这样做,但它不符合C/C++标准。错误的示例:#include "stdio.h"、#include "iostream"。
那么,在C++中头文件是怎么定义的呢?
在语句“#include<iostream>”中,iostream.h就是头文件。C++程序的头文件是以“.h”为后缀的,用于保存程序的声明。
一个头文件由3部分内容组成:
●头文件开头处的版权和版本声明。
●预处理块。
●函数和类结构声明等。
在C++中,头文件的作用主要包含以下两点。
(1)可以通过头文件来调用已有的程序功能。为了保护源代码的安全性,通过头文件的形式来调用该代码的功能。用户只需要按照头文件中的接口声明来调用该头文件中的功能,而不必关心具体功能是怎么实现的。编译器会从库中读取相应的代码。
(2)头文件可以加强安全性检查。在调用接口功能时,如果调用方式和头文件中的声明不一致,编译器就会报错,从而减少程序员的调试负担。
提示
不要使用#include <iostream.h>,不要使用#include <string.h>,因为它们已经被C++标准明确地废弃了,请改为#include <iostream>和#include <cstring>,规则如下。
(1)如果这个头文件是旧C++特有的,那么去掉.h后缀,并放入std名字空间,如iostream.h变为iostream。
(2)如果这个头文件是C也有的,那么去掉.h后缀,增加一个c前缀,如string.h变为cstring、stdio.h变为cstdio等。
2.2.2 main函数
在上例中,使用了main()函数,那么这个main()函数代表什么呢?C++程序必须有且只能有一个main()函数,main()函数是程序的入口点,无论main函数在程序中处于什么样的位置。但是,并非所有C++程序都有传统的main()函数。用C或C++写成的Windows程序入口点函数称为WinMain(),而不是传统的main()函数。
main()函数和其他函数一样也是函数,有相同的构成部分。在32位控制台应用程序中,C++Builder生成具有下列原型的默认main()函数:int main(int argc,char** argv);。这个main()函数形式取两个参数并返回一个整型值。
提示
不要将main函数的返回类型定义为void,虽然有些编译器允许你这样做,但它不符合C/C++标准。不要将函数的int返回类型省略不写,在C++中要求编译器至少给一个警告。错误的示例:void main(){}、main(){}。
第一个参数argc,argc代表参数的数量,指明有多少个参数将被传递给主函数main()。真正的参数以字符串数组(即第2个参数argv[])的形式来传递。
main()函数本身以索引0为第一参数,所以argc至少为1。它的总数是argv列阵的元素数目。这意味着argv[0]的值是至关重要的,如果用户在控制台环境中程序名称后输入含参数的指令,那么随后的参数将传递给argv[1]。
下面用一个实例来说明main如何调用参数。
【实例2-2】main函数调用参数(代码2-2.txt)
新建名为“Maintest”的【C++Source File】源程序,源代码如下所示:
【代码详解】
首先,在主程序中定义了三个double类型的变量。第一个变量Operand1对传入参数数组的第一个数取整,第二个变量Operand2对传入参数数组的第二个数取整,定义第三个变量Addition为前两个变量的和。
将第一个变量Operand1和第二个变量Operand2输出,然后将第三个变量Addition也输出。
在Visual Studio 2019主界面,选择【生成】|【生成Maintest】菜单选项,即可生成可执行文件Maintest.exe。在【DOS】窗口中执行Maintest.exe文件,输入两个参数12.5和36.8,运行结果如图2-2所示。
图2-2 代码运行结果
【实例分析】
在本例中,首先编译了该程序,生成Maintest.exe可执行文件。在调用可执行文件时,输入两个参数,分别是12.5和36.8,最后输出的结果是按照程序设计输出的。该例通过调用main函数实现上述功能。
2.2.3 变量声明和定义
在C++中,不仅变量有名字,枚举(enumeration)、函数(function)、类(class)、模板(template)等事务都有名字。在使用任何一个名字之前,必须先对该名字表示的事务进行声明(declaration)或者定义(definition)。在程序使用中,离不开变量。变量的定义可以为变量分配存储空间,还可以为变量指定初始值。在程序中,变量有且仅有一个定义。
声明是为了说明变量的类型和名字。定义也是声明,当定义变量的时候声明了它的类型和名字。可以通过使用extern关键字声明变量名而不定义它。不定义变量的声明包括对象名、对象类型和对象类型前的关键字extern。extern声明不是定义,也不分配存储空间。它只是说明变量定义在程序的其他地方,程序中变量可以声明多次,但只能定义一次。
例如:
在上例中,就是一个单纯的声明,而不是定义。这条语句只是告诉程序有一个int型变量i,而没有为i分配空间,也没有给i赋值。
任何在多文件中使用的变量都需要有与定义分离的声明。在这种情况下,一个文件含有变量的定义,使用该变量的其他文件则包含该变量的声明(而不是定义)。
可以用下面的语法来定义(也是声明)一个变量。
其中,变量类型说明符的作用是告诉编译器该变量的类型。表2-1列出了C++中的一些基本数据类型。
表2-1 C++中的一些基本数据类型
在定义变量时,需要遵循以下规则。
(1)变量名的首字母必须为26个英文字母的大小写加下画线,其他字母必须为26个英文字母的大小写加下画线和数字。
(2)变量名不可以是C++中预留的关键词。前面已经介绍过一些关键词,例如signed、unsigned、int和double等。
(3)C++标准规定,所有以两个下画线开头的名字,以及一个下画线加上一个大写字母开头的名字,例如__range、__Range或者_Range,在程序中都不可以用,因为要为标准库预留;所有以一个下画线开头并且第二个字符并不是下画线,也不是大写字母的名字,例如_range,在程序中不可以用在全局名字空间(对变量来说,在全局名字空间的变量也是全局变量)。全局变量和全局名字空间稍后再提。如果你用了这些名字,编译器可能不会报错,但是你的程序的可移植性就变差了,因为换到另一个编译器,就可能和另一个编辑器的库实现存在名字冲突。
提示
使用C++变量作用域时一定要注意,一般是以一对花括号范围作为一个作用域的。
(4)在C++中,名字的大小写是不同的,即大写字母的名字和小写字母的名字是不同的名字。例如,age、Age、AGE是3个不同的名字。
2.2.4 函数的声明
在上面的实例中,定义了一个函数main。其实在C++中,函数声明不仅仅是main函数。在C++程序中调用任何函数之前,首先要对函数进行定义。如果调用此函数在前,定义函数在后,就会产生编译错误。
为了使函数的调用不受函数定义位置的影响,可以在调用函数前进行函数的声明。这样,无论函数是在哪里定义的,只要在调用前进行函数的声明,就可以保证函数调用的合法性。
在C++中,函数的定义格式为:
提示
参数的书写要完整,不要图省事只写参数的类型而省略参数名字。如果函数没有参数,就用void填充。另外,参数命名要恰当,顺序要合理。
通常,函数名可以是任何合法的标识符。函数的参数列表是可选的,如果函数不需要参数,就可以省略参数列表,但是参数列表两边的括号不能省略。
函数体描述的是函数的功能,主要由一条或多条语句构成。函数也可以没有函数体,此时的函数称为空函数。空函数不执行任何动作。在开发程序时,当前可能不需要某个功能,但是将来可能需要,此时可以定义一个空函数,在需要时为空函数添加实现代码。
函数都有一个返回值,当函数结束时,将返回值返回给调用该函数的语句。但是,函数也可以没有返回值,即返回值类型为void。如果函数有返回值,通常在函数体的末尾使用return语句返回一个值,其类型必须与函数定义时的返回值类型相同或兼容。
下面用一个简单的实例来说明函数如何声明和定义。
【实例2-3】函数应用(代码2-3.txt)
新建名为“maxtest”的【C++ Source File】源程序,源代码如下所示:
【代码详解】
在这个例子中,首先定义了main函数,在main函数中声明了min函数,之后声明了3个变量m、n和Min。然后,调用cin,输入两个float类型的数值,分别复制给a和b。最后调用min函数找到两个数中较小的数字,并将该值输出。
运行结果如图2-3所示。
图2-3 代码运行结果
【实例分析】
首先输入一个数为150,接着输入260。根据程序设计,取150和260中较小的数,将较小的数输出。
2.2.5 关于注释
在C++中,注释是用来帮助程序员读程序的语言结构,是一种程序礼仪,可以用来概括程序的算法、表达变量的意义或者阐明一段比较难懂的程序代码。注释不会增加程序的可执行代码的长度。在代码生成以前,编译器会将注释从程序中剔除掉。
提示
说明性文件(如头文件(.h文件)、.inc文件、.def文件、编译说明文件(.cfg文件)等)头部应进行注释,注释必须列出,如版权说明、版本号、生成日期、作者、内容、功能、与其他文件的关系、修改日志等,头文件的注释中还应有函数功能简要说明。
C++中有两种注释符号,一种是注释对(/*,*/),与C语言中的一样。注释的开始用/*标记,编译器会把/*与*/之间的代码当作注释。注释可以放在程序的任意位置,可以含有制表符(tab)、空格符或换行符,还可以跨越多行程序。
例如:
另一种注释符是双斜线(//),它可以用来注释一个单行,程序行中注释符右边的内容都将被当作注释而被编译器忽略。
例如:
在C++中,注释的种类分为以下几种。
●重复性注释:只是用不同文字把代码的工作又描述一次。这种代码对程序本身并没有提供更多信息。
●解释性注释:通常用于解释复杂、敏感的代码块。对于复杂的代码,需要写注释来说明该代码的功能,以增加程序的可读性。
●标记性注释:用于告诉开发者某处的工作未做完。在实际工作中,经常会以这些注释作为程序骨架的占位符,或者已知bug的标记。
●概述性注释:将若干行代码以一两句话说出来,程序员能够快速读取注释,了解程序的功能,增加可读性。
●意图性注释:用来指出要解决的问题,而非解决的方法。意图性注释和概述性注释没有明显的界限,其差异也无足轻重,都是非常有效的注释。
有些信息不能通过代码来体现,比如版权声明、作者、日期、版本号等信息以及与代码设计有关的一些注意事项,但是这些信息都必须体现在源代码中,所以就用注释来记录这些信息。
写好一份注释,是写出完美程序的前提,写好注释并不比写好一段程序更容易。所以在写注释的过程中,必须遵循以下几个原则。
(1)站在读者的立场编写注释
编写的代码将会面对很多不同的读者。其中还包括代码的复审者。他们希望看到的是准确的注释。注释的内容应包含:源程序的特性(文件名、作用、创建时间等)和函数注释。
(2)及时编写注释
注释应该是在编程的过程中同时进行的,不能在程序开发完成之后再补写注释。这样会多花很多时间,并且在长时间之后,会慢慢读不懂自己的程序了。
(3)好注释能在更高抽象层次上解释想干什么
在编写注释这一问题上,经常犯的一个错误是将代码已经清楚说明的东西换种说法再写一次。如果为代码添加中文注释的时候,简单地等同于将英文译作中文,那么这样的注释能够给他人带来的好处微乎其微,更多时候是徒增阅读负担以及维护工作量。