09-使用接口
17.3.3 使用接口
我们的目标是,使用这个接口编写程序,但是不必知道具体的实现细节(如,不知道函数的实现细节)。在编写具体函数之前,我们先编写电影程序的一个新版本。由于接口要使用 List 和 Item 类型,所以该程序也应使用这些类型。下面是编写该程序的一个伪代码方案。
创建一个 List 类型的变量。
创建一个 Item 类型的变量。
初始化链表为空。
当链表未满且有输入时:
把输入读取到 Item 类型的变量中。
在链表末尾添加项。
访问链表中的每个项并显示它们。
程序清单17.4中的程序按照以上伪代码来编写,其中还加入了一些错误检查。注意该程序利用了 list.h (程序清单17.3)中描述的接口。另外,还需注意,链表中含有 showmovies() 函数的代码,它与 Traverse() 所要求的原型一致。因此,程序可以把指针 showmovies 传递给 Traverse() ,这样 Traverse() 可以把 showmovies() 函数应用于链表中的每一项(回忆一下,函数名是指向该函数的指针)。
程序清单17.4 films3.c 程序
/* films3.c -- 使用抽象数据类型(ADT)风格的链表 */
/* 与list.c一起编译 */
#include <stdio.h>
#include <stdlib.h> /* 提供exit()的原型 */
#include "list.h" /* 定义List、Item */
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')
continue;
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("Movie: %s Rating: %d\n", item.title,
item.rating);
}
char * s_gets(char * st, int n)
{
char * ret_val;
char * find;
ret_val = fgets(st, n, stdin);
if (ret_val)
{
find = strchr(st, '\n'); // 查找换行符
if (find) // 如果地址不是NULL,
*find = '\0'; // 在此处放置一个空字符
else
while (getchar() != '\n')
continue; // 处理输入行的剩余内容
}
return ret_val;
}