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;
}


相关文章
|
3月前
|
存储 程序员 C语言
c++primer plus 6 读书笔记 第四章 复合类型
c++primer plus 6 读书笔记 第四章 复合类型
|
3月前
|
存储 编译器 C语言
C primer plus 学习笔记 第14章 结构和其他数据形式
C primer plus 学习笔记 第14章 结构和其他数据形式
R语言笔记丨字符串和列表必学基础知识
R语言笔记丨字符串和列表必学基础知识
C Primer Plus 第四章编程练习
C Primer Plus 第四章编程练习
96 0
C Primer Plus 第四章编程练习
C Primer Plus 第五章 编程练习
C Primer Plus 第五章 编程练习
87 0
C Primer Plus第七章编程练习
C Primer Plus第七章编程练习
60 0
|
存储 算法 编译器
高级数据表示(C Primer Plus 第六版)
高级数据表示(C Primer Plus 第六版)
73 0
|
安全 前端开发 测试技术
SystemVerilog学习-01-系统验证概述(一)
SystemVerilog学习-01-系统验证概述
290 0
SystemVerilog学习-01-系统验证概述(一)
|
监控 安全 搜索推荐
SystemVerilog学习-01-系统验证概述(二)
SystemVerilog学习-01-系统验证概述
379 0
SystemVerilog学习-01-系统验证概述(二)
|
C语言
C语言高级数据表示(C Primer Plus 第六版)(三)
C语言高级数据表示(C Primer Plus 第六版)(三)
134 0
C语言高级数据表示(C Primer Plus 第六版)(三)