本章内容较难,涉及到很多内容,其中很多都需要多文件编译
下面每一道题,我都将几个文本合在一起,注意看注释说明
no1.c
//修改程序清代17.2,让该程序既能正序也能逆序显示电影列表.一种方法是修改链表的定义.
//可以双向遍历链表,另一种方法是用递归.
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define TSIZE 45
struct film
{
char title[TSIZE] ;
int rating ;
struct film * next ;
};
char * s_gets(char * st , int n);
void reverse_print(const struct film * ps); //添加逆序输出的函数
int main(void)
{
struct film * head = NULL ;
struct film * prev , * current ;
char input[TSIZE] ;
puts("Enter first movie title :");
while (s_gets(input , TSIZE) != NULL && input[0] != '\0')
{
current = (struct film *)malloc(sizeof(struct film)) ;
if (head == NULL)
head = current ;
else
prev->next = current ;
current->next = NULL ;
strcpy(current->title , input);
puts("Enter your rating <0 - 10> :");
scanf("%d" , ¤t->rating);
while (getchar() != '\n') ;
puts("Enter next movie title (empty line to stop) :");
prev = current ;
}
if (head == NULL)
printf("No data entered.");
else
printf("Here is the movie list :\n");
current = head ;
while (current != NULL)
{
printf("Movie: %s Rating : %d\n" , current->title , current->rating);
current = current->next ;
}
//no1.c 修改17.2程序清单 添加逆序输出的代码,使用递归方式
puts("==============================================");
//reverse_print(head);
// 切记切记 一定不要忘记释放内存, 不然程序稍大一点内存泄露问题就会很严重
current = head ;
while (current != NULL)
{
/*原17.2程序清单 代码有错
free(current);
current = current->next ;
*/
head = head->next ; //先使头指针指向下一个节点
free(current); //释放掉当前指向的节点
current = head ; //当前指针指向头指针
}
printf("Bye!\n");
return 0 ;
}
char * s_gets(char * st , int n)
{
char * ret_val ;
char * find ;
if (ret_val = fgets(st , n , stdin))
{
if (find = strchr(st , '\n'))
*find = '\0' ;
else
while (getchar() != '\n') ;
}
return ret_val ;
}
//逆序输出(递归)
//ps指向链表
void reverse_print(const struct film * ps)
{
if (ps->next != NULL)
reverse_print(ps->next);
printf("Movie: %s Rating : %d\n" , ps->title , ps->rating);
}
no2.c
/*no2.c文件如下*/
/*注意需要和list.c文件一起编译*/
//假设 list.h(程序清单17.3)使用下面的list定义
//typedef struct list
//{
// Node * head ;
// Node * end ;
//}List ;
//重写list.c(程序清单17.5)中的函数以适应新的定义,并通过films.c(程序清单17.4)
//测试最终代码
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include "list.h"
void showmovies(Item item) ;
char * s_gets(char * st , int n);
int main(void)
{
List movies ;
Item temp ;
InitializeList(&movies);
if (ListIsFull(&movies))
{
fprintf(stderr , "No Memory available! Bye!\n");
exit(1);
}
puts("Enter first movie title:");
while (s_gets(temp.title , TSIZE) != NULL && temp.title[0] != '\0')
{
puts("Enter your rating <0-10>:");
scanf("%d" , &temp.rating);
while (getchar() != '\n') ;
if (AddItem(temp , &movies) == false)
{
fprintf(stderr , "Problem allocating memory\n");
break ;
}
if (ListIsFull(&movies))
{
puts("The list is now full.");
break ;
}
puts("Enter next movie title (empty line to stop):");
}
if (ListIsEmpty(&movies))
{
printf("No data entered.");
}
else
{
printf("Here is the movie list:\n");
Traverse(&movies , showmovies);
}
printf("You entered %d movies.\n" , ListItemCount(&movies));
EmptyTheList(&movies) ;
printf("Bye! \n");
return 0 ;
}
void showmovies(Item item)
{
printf("Movies: %s Rating: %d\n" , item.title , item.rating);
}
char * s_gets(char * st , int n)
{
char * ret_val = NULL ;
char * find = NULL ;
if (ret_val = fgets(st , n , stdin))
{
if (find = strchr(st , '\n'))
*find = '\0' ;
else
while (getchar() != '\n') ;
}
return ret_val ;
}
/*list.h文件*/
//类型名: 简单链表
//类型属性: 可以储存一系列的项
//类型操作: 初始化列表为空
// 确定链表为空
// 确定链表已满
// 确定链表中的项数
// 在链表末尾添加项
// 遍历链表,处理链表中的项
// 清空链表
# ifndef LIST_H
# define LIST_H
# include <stdbool.h>
# define TSIZE 45
struct film
{
char title[TSIZE];
int rating ;
};
typedef struct film Item ;
typedef struct node
{
Item item ;
struct node * next ;
}Node ;
/* no2.c 修改代码 重新定义List类型
typedef Node * List ;
*/
// no2.c 修改代码如下
typedef struct list
{
Node * head ;
Node * end ;
}List ;
//操作: 初始化链表
//前提条件: plist指向一个链表
//后置条件: 链表初始化为空
void InitializeList(List * plist);
//操作: 确定链表是否为空定义,plist指向一个已初始化的链表
//后置条件: 如果链表为空,该函数返回true,否则返回false
bool ListIsEmpty(const List * plist);
//操作: 确定链表是否已满
//后置条件: 如果链表已满,该函数返回true,否则返回false
bool ListIsFull(const List * plist);
//操作: 确定链表中的项数,plist指向一个已初始化的链表
//后置条件: 该函数返回链表中的项数
unsigned int ListItemCount(const List * plist);
//操作: 在链表末尾添加项
//前置条件: item是一个待添加的项,plist指向一个已初始化的链表
//后置条件: 如果可以,该函数在链表末尾添加一个项,且返回true,否则返回false
bool AddItem(Item item , List * plist);
//操作: 把函数作用于链表中的每一项
// plist指向一个已初始化的链表
// pfun指向一个函数,该函数接受一个Item类型的参数,且无返回值
//后置条件: pfun指向的函数作用于链表中的每一项一次
void Traverse(const List * plist , void (*pfun)(Item item));
//操作: 释放已分配的内存(如果有的话)
// plist指向一个已初始化的链表
//后置条件: 释放了为链表分配的所有内存,链表设置为空
void EmptyTheList(List * plist);
# endif
/*list.c文件*/
/*注意需要和no2.c文件一起编译*/
# include <stdio.h>
# include <stdlib.h>
# include "list.h"
static void CopyToNode(Item item , Node * pnode);
void InitializeList(List * plist)
{
/* no2.c 修改代码
*plist = NULL ;
*/
// no2.c 修改初始化代码如下
plist->head = plist->end = NULL ;
}
bool ListIsEmpty(const List * plist)
{
/* no2.c 修改代码
return (*plist == NULL) ;
*/
// no2.c 修改代码如下
return ((plist->head == NULL) && (plist->end == NULL));
}
// 此函数不需要修改,它只是尝试着分配了一个空间,去判断磁盘是否已满
// 并无最大限
bool ListIsFull(const List * plist)
{
Node * pt ;
bool full = false ;
pt = (Node *)malloc(sizeof(Node));
if (pt == NULL)
full = true ;
else
full = false ;
free(pt);
return full ;
}
unsigned int ListItemCount(const List * plist)
{
unsigned int count = 0 ;
/* no2.c 修改代码 节点指针指向头节点
Node * pnode = *plist ;
*/
// no2.c 修改代码如下
Node * pnode = plist->head ;
while (pnode != NULL)
{
++count ;
pnode = pnode->next ;
}
return count ;
}
bool AddItem(Item item , List * plist)
{
Node * pnew ;
/* no2.c 修改代码 不需要scan节点指针
Node * scan = *plist ;
*/
pnew = (Node *)malloc(sizeof(Node)) ;
if (pnew == NULL)
return false ;
CopyToNode(item , pnew);
pnew->next = NULL ;
/* no2.c 修改代码 判断头是否为空
* 如果为空,头指向新节点
* 否则在最后一个节点指向新节点
if (scan == NULL)
*plist = pnew ;
else
{
while (scan->next != NULL)
scan = scan->next ;
scan->next = pnew ;
}
*/
if (plist->head == NULL)
plist->head = plist->end = pnew ;
else
{
plist->end->next = pnew ;
plist->end = plist->end->next ;
}
return true ;
}
void Traverse(const List * plist , void (*pfun)(Item item))
{
/* no2.c 修改代码 pnode 指向头节点
Node * pnode = *plist ;
*/
// no2.c 修改代码如下
Node * pnode = plist->head ;
while (pnode != NULL)
{
(*pfun)(pnode->item);
pnode = pnode->next ;
}
}
void EmptyTheList(List * plist)
{
Node * psave ;
while (plist->head != NULL)
{
/* no2.c 修改代码 psave 每次指向头节点的下一个节点
psave = (*plist)->next ;
free(*plist);
*plist = psave ;
*/
// no2.c 修改代码如下
psave = plist->head->next ;
free(plist->head);
plist->head = psave ;
}
}
static void CopyToNode(Item item , Node * pnode)
{
pnode->item = item ;
}
no3.c
/*no3.c文件*/
/*注意需要和list.c文件一起编译*/
//假设list.h(程序清单17.3)使用下面的list定义
//# define MAXSIZE 100
//typedef struct list
//{
// Item entrise[MAXSIZE];
// int items ;
//}List ;
//重写list.c(程序清单17.5)中的函数以适应新的定义,并通过films.c(程序清单17.4)
//测试最终代码
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include "list.h"
// no3.c 修改代码
//void showmovies(Item item) ;
// no3.c 重新声明函数
void showmovies(Item * item);
char * s_gets(char * st , int n);
int main(void)
{
List movies ;
Item temp ;
InitializeList(&movies);
if (ListIsFull(&movies))
{
fprintf(stderr , "No Memory available! Bye!\n");
exit(1);
}
puts("Enter first movie title:");
while (s_gets(temp.title , TSIZE) != NULL && temp.title[0] != '\0')
{
puts("Enter your rating <0-10>:");
scanf("%d" , &temp.rating);
while (getchar() != '\n') ;
if (AddItem(temp , &movies) == false)
{
fprintf(stderr , "Problem allocating memory\n");
break ;
}
if (ListIsFull(&movies))
{
puts("The list is now full.");
break ;
}
puts("Enter next movie title (empty line to stop):");
}
if (ListIsEmpty(&movies))
{
printf("No data entered.");
}
else
{
printf("Here is the movie list:\n");
Traverse(&am