C primer plus 学习笔记 第17章 高级数据表示

简介: C primer plus 学习笔记 第17章 高级数据表示

第17章 高级数据表示

 

17.1 研究数据表示

假设要编写一个程序,让用户输入一年内看过的电影,存储影片的信息。

可以使用结构储存电影,用结构数组存储多部电影。

但给数组分配空间时,会出现分配空间过大浪费或者分配空间过小不够用的问题。

使用动态内存(malloc)分配可以解决这个问题。

17.2 从数组到链表

struct file {

char title[TSIZE];

int rating;

struct film * next;

}

struct file *head;

链表看起来是一个特殊的结构,他包含自己的数据,还有下一个结构的地址. 另外有一个单独的head指针来标识开头地址。

用户输入时可以先给第一个结构分配内存空间,然后将他的next的地址赋值为NULL(空,因为现在他后面没有结构了)

在给第二个结构分配内存,然后将第一个结构的next指针指向第二个元素,将第二个元素的next设置为空......

具体用法见示例:

17.2.1 使用链表

 

#define _CRT_SECURE_NO_WARNINGS
#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);
 
 
int main(void)
{
  struct film * head = NULL;
  struct film * prev = NULL, *current = NULL;
  char input[TSIZE];
 
  puts("输入第一部电影的名字:");
  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("输入评分<0-10>:");
    scanf("%d", &current->rating);
    while (getchar() != '\n')
      continue;
    puts("输入下一部电影名字(直接回车可退出)");
    prev = current;
 
  }
  //显示电影
  if (head == NULL)
    printf("无数据.");
  else
  {
    printf("电影列表如下:\n");
    current = head;
    while (current != NULL)
    {
      printf("电影:%s 评分:%d\n", current->title, current->rating);
      current = current->next;
    }
  }
 
  //释放内存
  current = head;
  while (head != NULL)  //此处和书不同,书上运行出错。我认为这里应该判断head是否NULL而不是current是否为NULL
  {
    current = head;
    head =head->next;
 
    free(current);
  }
  printf("BYE\n");
 
  return 0;
}
 
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)
      *find = '\0'; //将换行符换成'\0'
    else
      while (getchar() != '\n')  //处理输入行剩余的字符
        continue;
  }
  return ret_val;
}


 

17.3 抽象数据类型(ADT,abstract data tye)

用更系统的方法定义数据类型:

类型特指两类信息:属性和操作。 //容易想到C++的类

 

17.3.1 建立抽象

17.3.2 建立接口

接口有两个部分:数据和操作

 

//下面是链表是具体实现

 

//list.h

#pragma once 
#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;
typedef Node * List;
 
/*函数原型*/
 
/*操作:   初始化一个链表   */
/*前提条件: plist指向一个链表 */
/*后置条件: 链表初始化为空   */
void InitializeList(List * plist);
 
/*操作:   确定链表是否为空定义,plist指向一个已初始化的链表 */
/*后置条件: 如果链表为空,返回ture;否则返回false       */
bool ListIsEmpty(const List * plist);
 
/*操作:   确定链表是否已满,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);

//list.cpp

#include<stdio.h>
#include<stdlib.h>
#include"list.h"
 
static void CopyToNode(Item item, Node * pnode);
 
void InitializeList(List * plist)
{
  *plist = NULL;
}
 
bool ListIsEmpty(const List * plist)
{
  if (*plist == NULL)
    return true;
  else
    return  false;
}
 
bool ListIsFull(const List * plist)
{
  Node * pt;
  bool full;
  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;
  Node * pnode = *plist;
  while (pnode != NULL)
  {
    ++count;
    pnode = pnode->next;
  }
  return count;
}
 
bool AddItem(Item item, List * plist)
{
  Node * pnew;
  Node * scan = *plist;
  pnew = (Node *)malloc(sizeof(Node));
  if (pnew == NULL)
    return false;
  CopyToNode(item, pnew);
  pnew->next = NULL;
  if (scan == NULL)
    *plist = pnew;
  else
  {
    while (scan->next != NULL)
      scan = scan->next;
    scan->next = pnew;
  }
 
  return true;
}
 
void Traverse(const List * plist, void(*pfun)(Item item)) 
{
  Node * pnode = *plist;
  while (pnode!= NULL)
  {
    (*pfun)(pnode->item);
    pnode = pnode->next;
  }
}
 
void EmptyTheList(List * plist)
{
  Node * psave;
  while (*plist != NULL)
  {
    psave = (*plist)->next;
    free(*plist);
    *plist = psave;
  }
}
static void CopyToNode(Item item, Node * pnode)
{
  pnode->item = item;
}

film3.c

/*film3.c            */
/*与list.c一起编译        */
#define _CRT_SECURE_NO_WARNINGS
#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, "无可用内存,告辞。\n");
    exit(1);
  }
 
  /*获取用户输入 并存储*/
  puts("输入第一个电影名称:");
  while (s_gets(temp.title, TSIZE) != NULL && temp.title[0] != '\0')
  {
    puts("输入你的评分<0-10>:");
    scanf("%d", &temp.rating);
    while (getchar() != '\n')
      continue;
    if (AddItem(temp, &movies) == false)
    {
      fprintf(stderr, "分配内存出错\n");
      break;
    }
    if (ListIsFull(&movies))
    {
      puts("列表满了.");
      break;
    }
    puts("输入下一步电影名称(回车结束程序)");
  }
 
  /*显示*/
  if (ListIsEmpty(&movies))
    printf("列表为空");
  else 
  {
    printf("Here is the movie list:\n");
    Traverse(&movies, showMovies);
  }
  printf("你输入了%d个电影\n", ListItemCount(&movies));
 
  /*清理*/
  EmptyTheList(&movies);
  printf("再见\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)
      *find = '\0'; //将换行符换成'\0'
    else
      while (getchar() != '\n')  //处理输入行剩余的字符
        continue;
  }
  return ret_val;
}


相关文章
|
人工智能 搜索推荐 数据挖掘
未来教育行业的就业前景如何?
【8月更文挑战第4天】未来教育行业的就业前景如何?
382 1
|
Go 开发工具 Python
【开发工具】Goland 2022.4 破解(by ja-netfilter)
【开发工具】Goland 2022.4 破解(by ja-netfilter)
881 1
【开发工具】Goland 2022.4 破解(by ja-netfilter)
|
弹性计算 安全 Ubuntu
新手3分钟1Panel安装教程,使用阿里云服务器CentOS操作系统
在阿里云CentOS 7.9服务器上安装1Panel面板,包括远程连接ECS、执行安装命令、设置安装目录(默认/opt)、开启20410端口、配置安全入口和用户密码。记得在阿里云安全组中开放20410端口以访问面板。
1145 0
新手3分钟1Panel安装教程,使用阿里云服务器CentOS操作系统
|
小程序 Java 数据库
微信小程序的驾校预约管理系统
微信小程序的驾校预约管理系统
|
存储 人工智能 开发工具
Git 中文参考(五)(9)
Git 中文参考(五)
219 2
|
SQL 监控 Shell
Linux AWK实战
Linux AWK实战
202 0
|
消息中间件 SQL 搜索推荐
基于Confluent+Flink的实时数据分析最佳实践
在实际业务使用中,需要经常实时做一些数据分析,包括实时PV和UV展示,实时销售数据,实时店铺UV以及实时推荐系统等,基于此类需求,Confluent+实时计算Flink版是一个高效的方案。
768 1
基于Confluent+Flink的实时数据分析最佳实践
|
网络协议 网络安全 区块链
北京大学肖臻老师《区块链技术与应用》公开课笔记6——比特币网络
北京大学肖臻老师《区块链技术与应用》公开课笔记6——比特币网络
396 0
|
存储 缓存 自然语言处理
小刚带你深入浅出理解Lua语言
前言这篇文章并不是针对某个知识点深入剖析,而是聚焦在Lua语言的关键知识点覆盖和关键使用问题列举描述。能够让学习者对Lua整体有个认识(使用一门新的语言不仅仅在用的时候适应它,而是知道怎么善于使用它),同时也可以作为一个工具文档在Lua中遇到具体问题的时候能从这里索引到相应的知识点和Lua的一些原理,得到启发。 1、Lua语言的特点简单的说Lua语言是一个可扩展的嵌入型的脚本语言。它具有以下的特点
小刚带你深入浅出理解Lua语言
|
Java Android开发
在Eclipse中安装e(fx)lipse (JavaFX工具)
从Java8开始,JDK(Java开发工具包)包括了JavaFX库。 因此,要运行JavaFX应用程序,您只需要在系统中安装Java8或更高版本。
2046 1