25.6 使用多文件编程

多文件编程考虑如何更好地实现结构化设计,是编制大文件的基础。这里给出一个使用动态内存建立循环链表,实现约瑟夫环的游戏的例子,本程序使用多文件编程。

【例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
  

《C语言解惑》