首先,来看一个随机数函数,该函数使用了一个具备内部连接的静态变量。ANSI C程序库提供了rand()函数来产生随机数。有多种随机数的算法,ANSI C 标准容许C实现使用针对特定机器的最佳算法。不过ANSI C也提供了一个可移植的标准算法,能够在不一样的系统中产生相同的随机数。事实上rand()是一个“伪随机数发生器”,这意味着能够预测数字的实际顺序(计算机不具备自发性),但这些数字在可能的取值范围内均匀地分布。算法
为了看清楚程序内部发生了什么, 咱们使用可移植的ANSI版本程序,而不是编译器内置的rand()函数。这一方案始于一个称为“种子”的数字。函数使用这个种子来产生一个新数,而这个新数又称为新的种子。接着这个新种子被用来产生一个更新的种子,依此类推。这种方案要想行之有效,随机数函数必须记下上次被调用时所使用的种子。对,这须要一个静态变量。程序清单12.7中的程序是版本0(很快您将看到版本1)。函数
程序清单12.7 rand0.c 函数文件测试
/* rand0.c --产生随机数 */ /* ANSI C 的可移植算法 */ static unsigned long int next = 1; /*种子*/ int rand0(void) { /*产生伪随机数的魔术般的公式 */ next = next * 1103515245 + 12345; return (unsigned int)(next/65536) % 32768; }
在程序清单12.7中静态变量next的初始值为1,在每次调用函数时它的值被一个魔术般的公式修改。结果是一个从0到32767范围内的返回值。注意next是静态、具备内部连接的,而不仅是静态、空连接的。这是为了稍后在将本例扩展时,便于next为同一文件中的两个函数共享。ui
咱们用程序清单12.8所示的简单驱动程序来测试一下rand0()函数指针
程序清单12.8 r_drive0.c驱动程序code
/*r_drive0.c --测试rand0()函数*/ /*与rand0.c一块儿编译*/ #include <stdio.h> extern int rand0(void); int main(void) { int count; for(count=0;count<5;count++) printf("%hd\n",rand0()); return 0; }
如今又有一个机会来练习使用多文件。程序清单12.7和程序清单12.8分别使用一个文件。关键字extern提醒您rand0()在一个单独的文件中定义。对象
输出以下:作用域
16838 5758 10113 17515 31051
输出看起来像随机的。但让咱们再来运行一次,此次结果以下:原型
16838 5758 10113 17515 31051
看起来很像,这就是“伪”的特征了。每次运行主程序时,都从同一个种子值1开始。能够经过引入容许重置种子的第二个函数srand1()来解决这个问题。关键是使next成为一个具备内部连接的静态变量,亲只对rand1()和srand1()可见(C程序库中与srand1()等效的函数被称为srand())。把srand1()添加到包含rand1()的文件中。程序清单12.9给出了修改后的程序。编译器
/*s_and_r.c --包含函数rand1()和srand1()的文件*/ /*使用ANSI C 的可移植算法*/ static unsigned long int next = 1; /*种子*/ int rand1(void) { /*产生随机数的魔术般的公式*/ next = next*1103515245 + 12345; return (unsigned int)(next/65536)%32768; } void srand1(unsigned int seed) { next = seed; }
注意next是一个具备内部连接的文件做用域变量。这意味着它能够同时被rand1()和srand1()使用,但不能够被其余文件中的函数使用。使用清单12.10中的驱动程序来测试这些函数。
程序清单12.10 r_drive1.c 程序
/* r_drive1.c 测试函数rand1()和srand1() */ /*与s_and_r.c 一块儿编译*/ #include <stdio.h> extern void srand1(unsigned int x); extern int rand1(void); int main(void) { int count; unsigned seed; printf("Please enter your choice for seed.\n"); while(scanf("%u",&seed)==1) { srand1(seed); /*重置种子*/ for(count=0;count<5;count++) printf("%hd\n",rand1()); printf("Please enter next seed(q to quit): \n"); } printf("Done\n"); return 0; }
又使用了两个文件。运行一次程序。
please enter your choice for seed. 1 16838 5758 10113 17515 31051 please enter your choice for seed: 3 20067 23475 8955 20841 15324 please enter your choice for seed: q Done
将1做为seed的值,产生了与前面相同的结果。如今来尝试将3做为seed的值:
若是您的C实现容许您访问系统时钟这种不断变化的量,能够用它们的值(可能须要截断)来初始化种子值。例如,ANSI C有一个函数time()能够返回系统时间。时间单位由系统决定,但有用的一点是返回值为数值类型,而且随着时间变化。其确切类型与系统有关,名称为time_t,但您能够对它进行类型指派。下面是基本思路:
#include <time.h> /*为time()函数提供ANSI原型*/ srand1((unsigned int) time(0)) /*初始化种子*/
一般,time()的参数是一个time_t类型对象的地址。那种情形下,时间值也存储在那个地址中。然而,您也能够传送空指针(0)做为参数。此时,时间值仅经过返回值机制提供。
能够对标准的ANSI C函数srand()和rand()使用一样的技术。使用这些函数地,要包括stdlib.h头文件。实际上,既然已经知道srand1()和rand1()如何使用一个具备内部连接的静态变量,您一样也可使用您的编译器提供的版本。咱们将在下一个例子中这样作。