多文件编程考虑如何更好地实现结构化设计,是编制大文件的基础。这里给出一个使用动态内存建立循环链表,实现约瑟夫环的游戏的例子,本程序使用多文件编程。
【例25.7】传说有30个旅客同乘一条船,因为严重超载,加上风浪大作,危险万分。因此船长告诉乘客,只有将全船一半的旅客投入海中,其余人才能幸免于难。无奈,大家只得同意这种办法,并议定30个人围成一圈,由第一个人数起,依次报数,数到第9人,便把他投入大海中,然后再从他的下一个人数起,数到第9人,再将他扔进大海中,如此循环地进行,直到剩下15个乘客为止。问哪些位置是将被扔下大海的位置。由这个传说产生了约瑟夫环的游戏。
因为是用申请的连续内存区建立循环链表,所以大大简化了建立的过程。
不失一般性,将30改为一个任意输入的正整数number,而报数上限(也就是间隔数)为一个任选的正整数interval。
程序允许既可以输入名字,也可以输入编号,所以将结构设计为一个字符串数据和指针域即可。
typedef struct node{ char name[15] ; struct node * next ; }ListNode ;
因为程序要适应输入奇数的情况,所以规定人数为奇数时,生还者比出局者多1个。
主程序设计三个函数求解。
int main ( ) { Initial (); // 取得参加游戏的人数number 和间隔数interval SetRing (number ); // 根据参加游戏的人数number 建立循环链表 Find (); // 求解 return 0 ; }
求解函数Find包含两个函数,分别用来求解并输出出局者和生还者。
void Find () { FindOut (); // 求解并输出出局名单 PrintLeft (); // 输出生还者名单 }
建立工程Ring,在其中定义一个头文件Ring.h,并定义C文件Ring.c和main.c。
1.头文件Ring.h
使用标准的条件编译方法建立头文件。
/***************************** * 建立头文件 * *****************************/ #if !defined (RING_H ) #define RING_H #include <stdio.h> #include \"string.h\" #include <stdlib.h> struct person { char name[10] ; struct person *next ; } ; struct person *pBegin ; struct person *pCurrent ; struct person *pTmp ; int number ; // 参加人数 int interval ; // 间隔数 void Countx (int m ); // 数间隔数 void Dispx (); // 显示出局者 void Clsx (); // 删除出局的结点 void SetRing (int n ); // 建立循环链表 void Find (); // 求解 void Initial (); // 接受游戏的人数和间隔数 void FindOut (); // 找出并输出出局者 void PrintLeft (); // 输出存活者 #endif
2.源文件Ring.c
//Ring.c #include \"Ring.h\" /**************************** * SetRing 函数 * * 功能:建立循环链表 * * 参数:n 循环链表长度 * *****************************/ void SetRing (int n ) { int i ; char s[10] ; pBegin= (struct person * )malloc (n*sizeof (struct person )); pCurrent=pBegin ; for ( i=1 ; i<=n ; i++ , pCurrent=pCurrent->next ) { pCurrent->next=pBegin+i%n ; printf (\" 输入第%d 个人的名字:\" ,i ); gets (s ); strcpy (pCurrent->name ,s ); } pCurrent=&pBegin[n-1] ; } /***************************** * Countx 函数 * * 功能:间隔计数 * * 参数n : 间隔长度 * ******************************/ void Countx (int m ) { int i ; for (i=0 ; i<m ; i++ ) { pTmp=pCurrent ; pCurrent=pTmp->next ; } } /***************************** * Dispx 函数 * * 功能:输出出局者信息 * ******************************/ void Dispx () { printf (\"%s \" ,pCurrent->name ); } /***************************** * Clsx 函数 * * 功能:删除出局者结点 * ******************************/ void Clsx () { pTmp->next=pCurrent->next ; pCurrent=pTmp ; } /********************************* * Initial 函数 * * 功能:接受游戏的人数和间隔数 * * number :参加游戏的人数 * * interval :间隔数 * **********************************/ void Initial () { printf (\" 输入参加游戏的人数:\" ); scanf (\"%d\" ,&number ); printf (\" 输入间隔数:\" ); scanf (\"%d\" ,&interval ); getchar (); } /*************************** * Find 函数 * * 功能:求解 * ****************************/ void Find () { FindOut (); // 求解并输出出局名单 PrintLeft (); // 输出生还者名单 } /*************************** * FindOut 函数 * * 功能:求解并输出出局名单 * ****************************/ void FindOut () { int i ; printf (\" 出局名单如下:n\" ); for (i=0 ; i<number/2 ; i++ ) { Countx (interval ); Dispx (); Clsx (); } printf (\"n\" ); } /*************************** * PrintLeft 函数 * * 功能:输出生还者名单 * ****************************/ void PrintLeft () { int i , tmp ; if ( number %2 == 0 ) tmp = number / 2 ; else tmp= (number +1 ) / 2 ; printf (\"n 生还者如下:n\" ); for (i=0 ; i<tmp ; i++ ) { pTmp=pCurrent ; pCurrent=pTmp->next ; Dispx (); } printf (\"n\" ); }
3.源文件main.c
//main.c #include \"Ring.h\" // 主函数 int main ( ) { Initial (); // 取得参加游戏的人数number 和间隔数interval SetRing (number ); // 根据参加游戏的人数number 建立循环链表 Find (); // 求解 return 0 ; }
4.组成工程
图25-1给出它的组成图。
5.运行实例
输入参加游戏的人数: 6 输入间隔数: 2 输入第1 个人的名字: 李一明 输入第2 个人的名字: 王小二 输入第3 个人的名字: 张 三 输入第4 个人的名字: 李 四 输入第5 个人的名字: 王老五 输入第6 个人的名字: Hob

图25-1 工程文件组成图
出局名单如下: 王小二 李 四 Hob 生还者如下: 张 三 李一明 王老五 输入参加游戏的人数: 5 输入间隔数: 2 输入第1 个人的名字: 李一明 输入第2 个人的名字: 王小二 输入第3 个人的名字: 张 三 输入第4 个人的名字: 李 四 输入第5 个人的名字: 王老五 出局名单如下: 王小二 李 四 生还者如下: 张 三 李一明 王老五 输入参加游戏的人数: 30 输入间隔数: 9 第1 个人的名字: 1 第2 个人的名字: 2 … // 省去中间过程,主要是验证后面的约瑟夫环程序 第29 个人的名字: 29 第30 个人的名字: 30 出局名单如下: 9 18 27 6 16 26 7 19 30 12 24 8 22 5 23 生还者如下: 25 28 29 1 2 3 4 10 11 13 14 15 17 20 21