ITEEDU

3.7 头文件

每个标准库都有对应的头文件(header file),包含库中所有函数的函数原型和这些函数所需各种数据类型和常量的定义。图3.6列出了C++程序中可能包括的常用C++标准库头文件。图3.6中多次出现的宏(macro)将在第17章“预处理器”中详细介绍。以.h结尾的头文件是旧式头文件。

对每个旧式头文件,我们介绍新标准中使用的版本。

程序员可以生成自定义头文件,自定义头文件应以.h结尾.可以用#include预处理指令包括自定义头文件。例如,square.h头文件可以用下列指令:

#include   "square.h"

放在程序开头。17.2节介绍了包含头文件的其他信息。

旧式头文件    说明

  旧式头文件(本书前面使用)
  (assert.h>    包含增加诊断以程序调试的宏和信息。这个头文件的新版本为<cassert>
  <ctype.h>     包含测试某些字符属性的函数原型和将小写字母变为大写字母,将大写字母变为小
                写字母的函数原型。这个头文件的新版本为<cctype>
  <float.h>     包含系统的浮点长度限制。这个头文件的新版本为<cfloat>
  <limits.h>    包含系统的整数长度限制。这个头文件的新版本为<climits>
  <math.h>      包含数学库函数的函数原型。这个头文件的新版本为<cmath>
  <stdio.h>     包含标准输入/输出库函数的函数原型及其使用信息。这个头文件的新版本为<cstdio>
  <stdlib.h>    包含将数字变为文本、将文本变为数字、内存分配、随机数和各种其它工具函数的函
                 数原型。这个头文件新版本为<cstdlib>
  <string.h>    包含C语言格式的字符串处理函数的函数原型。这个头文件的新版本为<cstring>
  <time.h>      包含操作时间和是期的函数原型和类型。这个头文件的新版本为<ctime>
  <iostream.h>  包含标准输入/输出函数的函数原型。这个头文件的新版本为<iostream>
  <iomanip.h>   包含能够格式化数据流的流操纵算子的函数原型。这个头文件的新版本为<iomanip>
  <fstream.h>   包含从磁盘文件输入输出到磁盘文件的函数原型。这个头文件的新版本为<fstream>
 

图 3.6 常用C++标准库头文件

3.8 随机数产生器

下面要介绍一个在模拟事件和游戏的程序中常用的组件。本节和下节开发一个结构良好、包括多个函数的游戏程序。程序中要使用前面介绍的大多数控制结构。

在赌场上,人人都关心的一个问题就是机会元素(element of chance),也就是赢钱的运气。这个机会元素可以用标准库中的rand函数引入计算机应用程序中。

考虑下列语句:

i=rand();  

rand函数产生O到RAND_MAX之间的整数(这是<stdlib.h>头文件中定义的符号常量)。RAND_MAX的值至少应为32767,也就是两个字节(即16位)所能表示的最大整数值。如果rand函数真的可以随机产生整数,则每次调用rand函数时,o到RAND_MAX之间的每个数出现的机会(chance)或概率(probability)是相等的。

rand函数产生的数值范围可能与特定应用中所要求的数值范围不同。例如,模拟掷硬币的程序只要0(正面)和1(反面),模拟投骰子的程序只要1到6之间的随机整数,视频游戏中预测飞船(有四种类型)下一个类型的程序只要1到4之间的随机整数。

要演示rand函数,我们开发一个程序,模拟投骰子20次并打印每次的值。rand函数的函数原型见 <stdlib.h>。我们使用求模运算符(%)和rand函数,如下所示:

rand( ) %   6  

产生0到5的整数,称为比例缩放(scaling)。数字6称为比例因子(scalingfactor)。然后我们将产生的数值范围加1。得到所要结果。图3.7确认了产生的结果在1到6之间。

要显示这些数值的出现机会近似均等.图3.8模拟投骰子6000次。1到6的每个数值大约都出

现1000次。

从程序输出可见,通过比例缩放和移动,可以利用rand函数真实地模拟投骰子。注意程序用不到switch结构中提供的default case,但我们还是加上default case,这是个良好的编程习惯。第4章数组将介绍如何把整个switch结构转换成简单的单行语句。

 // Fig. 3.7: figO307.cpp
 // Shifted, scaled integers produced by 1 + rand( ) % 6
 #include <iostream.h>
#include <iomanip.h>
#include <stdlib.h> int main( } for( int i = 1; i <= 20; i ++){ cout<< setw(10 ) << { 1 +rand( ) % 6 ); if( i % 5 == 0 ) cout << endl; } return O; }

输出结果:

5 5 3 5 5

2 4 2 5 5

5 3 2 2 1

5 1 4 6 4

图3.7 比例缩放和移动“rand()%6”产生的整数

  // Fig. 3.8:fig03 08.cpp
 // Roll a six-sided die 6000 times
 #include <iostream.h>
#include <iomanip.h>
#include <stdlib.h> int main( ) { int frequencyl = 0, frequency2 = 0, frequency3 = 0, frequency4 = 0, frequency5 = 0, frequency6 = 0, face; forf( int roll = 1; roll <= 6000; roll++ ) { ace = 1 + rand( ) % 6; switch ( face ) { case 1: ++frequencyl; break; case 2: ++frequency2; break; case 3: ++frequency3; break; case 4: ++frequency4; break; case 5: ++frequeneyS; break; case 6: ++frequency6; break; default: cout << "should never get here!"; } } cout << "Face" << setw{ 13 ) << "Frequency" << " 1" << setw( I3 ) << frequencyl << " 2" << setw( 13 ) << frequency2 << " 3" << setw( 13 ) << frequency3 << " 4" << setw( 13 ) << frequency4 << " 5" << setw( 13 ) << frequency5 << " 6" << setw( 13 ) << frequency6 << endl; return 0; }

输出结果:

Face Frequency

1 987

2 984

3 1029

4 974

5 1004

6 1022

图3.8模拟投骰子6000次

测试与调试提示3.1即使能确切保证程序没有缺陷,也应在switch结 构中用一个default case来找到错误。

再次执行图3.7的程序得到:

5 5 3 5 5

2 4 2 5 5

5 3 2 2 1

5 1 4 6 4

注意打印的数值顺序与上一次完全相同,怎么能算是随机数呢,其实,这种可重复性是rand函数的一个重要特性。调试程序时,这种可重复性提供了证明修改后程序能正确工作的关键。

rand函数实际上产生的是伪随机数(pseudo-randomnumber)。重复调用rand函数产生一系列看上去是随机的数值,但每次执行程序时,这组数值本身是可重复的。一旦程序进行彻底调试之后,就可以调整为在每次执行程序时产生不同的随机数系列。这个过程称为随机化(randomizing),是用标准库函数srand完成的。srand函数取一个unsigned类型的整数参数并内嵌rand函数(即种子),

就可以在每次执行程序时产生不同的随机数系列。

srand函数的使用如图3.9所示。在这个程序中,我们使用数据类型unsigned(unsigned int的缩写)。int值至少占内存的两个字节,可以存正值或负值。unsignediht变量值也至少占内存的两个字节。2字节的unsigned int只能取O到65535的非负值,4字节的unsignediht只能取O到4294967295的非负值,srand函数取unsigned int值作参数。srand函数的函数原型在头文件 <stdlibh>新C++标准的cstdlib)中。

下面将程序运行几次并观察结果。注意,只要提供不同的种子,即可在每次执行程序时产生不同的随机数系列。

// Fig. 3.9: fig0309.cpp
 // Randomizing die-rolling program

 #include <iostream.h>
#include <iomanip.h>
#include <stdlib.h> int main( ) { unsigned seed; cout << "Enter seed: "; cin >> seed; srand{ seed ); for ( int i = 1; i <= 10; i++ ) { cout << setw( 10 ) << 1 + rand( ) % 6; if ( i % 5 == 0 ) cout << endl; O } return 0; }

输出结果:

Enter seed: 67

1 6 5 1 4

5 6 3 1 2

Enter seed:432

4 2 6 4 3

2 5 1 4 4

Enter seed: 67

1 6 5 1 4

5 6 3 1 2

图3.9投骰子随机化程序

如果不想每次输入种子值而随机化,则要用如下语句:

srand( time ( O )   );  

使计算机通过时钟值自动取得种子值。time函数(上述语句中该函数的参数为0)返回当前“日历时间”的秒数,将这个值转换为无符号整数值,作为随机数产生器的种子。time函数的函数原型在<time>(新C++标准的ctime)中。

性能提示 3.2

srand函数只要在程序中调用一次即可得到所需的随机化结果,多次调用是多余的,会降低程序性能。

由rand函数直接产生的值总是取值为:

O≤rand()≤   RAND_MAX  

前面介绍了如何用一个语句模拟投骰子,该语句如下所示:

face=1+rand( ) % 6;  

总是对变量face指定1≤face≤6的整数(随机)。注意这个范围的宽度(即构成该范围的连续整数的个数)为6并从1开始。从上述语句可以看出,范围宽度是由求模运算符比例缩放rand的数值(6)确定的,开始值等于rand%6中加进的数值(即1)。可以将这个结果一般化,如下所示:

n = a + rand( ) %b;  

其中a是位移值(等于所要的连续整数范围的开始值),b是比例因子(即由连续整数构成的该范围的宽度)。练习中将介绍如何从一组非连续整数中随机选择整数。

常见编程错误3.16
用srnd函数代替rand函数产生随机数是个语法错误,因为srnd函数不返回值。