C语言 单链表下的结构体指针、函数指针、回调函数、多态、模拟面向对象编程

执行结果截图:

 代码:

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

//# define DEBUG
//# undef DEBUG

#ifdef DEBUG
# define PRINTF(templt, ...) fprintf(stderr, templt, ##__VA_ARGS__)
#else
# define PRINTF //
#endif

# define PRINT(format, ...) printf(# format, ##__VA_ARGS__)
# define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))

struct Book
{
char title[128];
char author[40];
struct Book * next;
} bookArray[3];

int getBooksInput(struct Book * *, int);
int printLibrary(struct Book * *, int);
int addBookHeadInsert(struct Book * *, int);
int addBookTailInsert(struct Book * *, int);
int manageLibrary(int (* fp)(struct Book * *, int), struct Book * *, int);
int (* SelectOperation(int count))(struct Book * *, int);
int releaseLibrary(struct Book * *, int);

int getBooksInput(struct Book * * bookPointer, int count)
{
struct Book * book = * bookPointer;

// 输入结构体变量的值并打印
PRINT(请输入书名 :);
scanf("%s", book->title);

PRINT(请输入作者 :);
scanf("%s", book->author);

count++;
PRINT(INFO: 已录入%d个图书信息\n, count);

return count;
}

int printLibrary(struct Book * * Library, int count)
{
struct Book * book = NULL;
int serialNumOfBook = 1;
// 将结构体指针book指向结构体(* library)
book = (* Library);
// 如果结构体指针是NULL,就报错
if (NULL == book)
{
PRINT(INFO: 没有可以打印的图书信息\n);
}
else
{
do
{
PRINT(Book%d ->, serialNumOfBook);
putchar(' ');
PRINT(书名 : %-20s, book->title);
putchar(' ');
PRINT(作者 : %-10s, book->author);
putchar('\n');
// 将结构体指针book指向链指针所指向的结构体
book = book->next;
serialNumOfBook++;
} while(NULL != book);
}

return count;
}

int releaseLibrary(struct Book * * library, int count)
{
// 定义一个结构体指针类型的临时变量temp,临时存放结构体指针(* library)的值
struct Book * temp;

// 只要结构体指针(* library)未指向NULL,那就意味着还未指向尾部结构体,
// 那就继续循环,让指针逐步接近直到指向尾部结构体,完成对整个链表的结构体的覆盖
while(NULL != * library)
{
// 将结构体指针temp指向结构体指针(* library)所指向的地址值,
// 以便不丢失已经申请的动态内存空间的地址,防止动态内存泄漏。
temp = * library;
/* 将结构体指针 * library指向单链表的下一个更接近尾部的结构体。
* 这时它不再指向原先指向的动态内存空间,不能用于释放原先指向的动态内存空间。
*/
* library = (* library)->next;
// 结构体指针temp存的是 * library原先指向的结构体地址,
// 释放这个结构体所对应的动态内存空间
free(temp);
}

PRINTF("Dynamic memory space got freed, struct pointer * library:%p\n", * library);
// 将结构体指针(* library)初始化为NULL
* library = NULL;
if (NULL == * library)
{
PRINTF("struct pointer * library: NULL\n");
}
else
{
PRINTF("struct pointer * library:%p\n", * library);
}
// count赋值为-1代表需要结束程序,-1会让主程序结束do while循环
count = -1;

return count;
}

int manageLibrary(int (* fp)(struct Book * *, int), struct Book * * library, int count)
{
// 将传址传入的整型函数指针fp用*解引用,即(* fp)(library, count),
// 根据fp当前所指向的函数,进行具体的函数调用。
return (int)(* fp)(library, count);
}

int (* SelectOperation(int count))(struct Book * * library, int count)
{
char selectOption;
int selectNum;

PRINT(=========================JohnnyH的图书信息管理系统============================\n);
PRINT(INFO:共可存储%d个书籍信息,当前已经录入了%d个书籍信息\n, ARRAY_SIZE(bookArray), count);
PRINT(1.录入书籍信息(头插法)\n2.录入书籍信息(尾插法)\n3.打印图书信息\n4.结束程序\n(请输入1或2或3或4对以上选项进行选择):\n);
do
{
selectOption = getchar();
} while('1' != selectOption && '2' != selectOption && '3' != selectOption && '4' != selectOption);
PRINTF("selectOption : %c\n", selectOption);

if ('1' == selectOption)
{
selectNum = 1;
}
else if ('2' == selectOption)
{
selectNum = 2;
}
else if ('3' == selectOption)
{
selectNum = 3;
}
else if ('4' == selectOption)
{
selectNum = 4;
}
PRINTF("selectNum : %d\n", selectNum);

switch(selectNum)
{
default:
return PRINT(操作选择失败\n);
case 1 :
PRINT(INFO:您选择了 1.录入书籍信息(头插法)\n);
// 返回一个函数指针
return addBookHeadInsert;
case 2 :
PRINT(INFO:您选择了 2.录入书籍信息(尾插法)\n);
// 返回一个函数指针
return addBookTailInsert;
case 3 :
PRINT(INFO:您选择了 3.打印图书信息\n);
// 返回一个函数指针
return printLibrary;
case 4:
PRINT(INFO:您选择了 4.结束程序\n);
// 返回一个函数指针
return releaseLibrary;
}
}

int addBookHeadInsert(struct Book * * library, int count)
{
struct Book * book = NULL;
struct Book * originalHead = NULL;

book = (struct Book *)malloc(sizeof(struct Book));
if (NULL == book)
{
PRINTF("ERROR!!!动态内存分配失败\n");
exit(1);
}

count = getBooksInput(&book, count);

PRINT(INFO: 将刚录入的图书信息插入链表(头插法)\n);
// 如果头指针(* library)的值不是NULL,那意味着(* library)已经指向了链表的原头部结构体,
// 因为是头插法,所以新插入的结构体就是链表的新的头部结构体,
// 把链指针book->next指向链表原头部结构体,并将头指针(* library)指向新插入的头部结构体book
if (* library != NULL) {
// 将结构体指针originalHead指向结构体指针(* library)所指向的原头部结构体
originalHead = * library;
// 将结构体指针(* library)指向新插入的头部结构体book,
* library = book;
// 将头部结构体book的链指针指向原头部结构体,
// 换句话说,从链表的头部插入了结构体book
book->next = originalHead;
}
else
{
// 如果头指针(* library)的值是NULL,那意味着新插入的结构体book既是头部结构体又是尾部结构体
// 那么就把头指针(* library)指向新插入的头部结构体book
* library = book;
// 链表的尾部结构体的特征是其链指针指向NULL,
// 因此将新插入的尾部结构体book的链指针book->next指向NULL
book->next = NULL;
}

PRINT(INFO: 完成录入\n);

return count;
}

int addBookTailInsert(struct Book * * library, int count)
{
struct Book * book = NULL;
static struct Book * tail = NULL;

book = (struct Book *)malloc(sizeof(struct Book));
if (NULL == book)
{
PRINTF("ERROR!!!动态内存分配失败\n");
exit(1);
}

count = getBooksInput(&book, count);

PRINT(INFO: 将刚录入的图书信息插入链表(尾插法)\n);

// 如果结构体指针(* library)的值不是NULL,那意味着(* library)已经指向了链表的头部结构体,
if (NULL != * library)
{
// 如果尾部结构体指针指向NULL,那么就将其指向不为NULL的头指针,
// 这样就可以确保尾部结构体指针指向了具体的结构体,
// 以便后续能利用具体结构体的链指针tail-next寻找尾部结构体,并将尾部结构体指针tail指向尾部结构体
if(NULL == tail)
{
tail = * library;
}
// 如果尾部结构体指针tail不指向NULL,且其链指针不指向NULL,那就意味着tail还未指向尾部结构体,
// 那么就设法让tail指向尾部结构体
if (NULL != tail)
{
// 用while循环判断条件判断是否tail指向了尾部结构体,如果没有就继续找
while(NULL != tail->next)
{
// 只要尾部结构体指针不指向NULL,那么就必然指向一个具体的结构体,
// 这个具体的结构体的链指针next可以表示为tail->next(因为指针tail当前正指向这个结构体),
// 接着就将指针tail指向当前结构体的链指针 tail->next所指向的下一个更接近尾部的结构体
// 这样指针tail就循着链指针tail->不断的向尾部挪动,直到指向当前的尾部结构体。
tail = tail->next;
}
PRINTF("struct pointer tail now points to the current Tail struct(before new struct inserts)\n");
}
// 因为是尾插法,那么新插入的结构体就是新的尾部结构体,
// 因此把原尾部结构体的链指针tail->next指向新插入的结构体book
tail->next = book;
PRINTF("struct pointer tail->next now points to struct book : %p\n", tail);
// 链表的尾部结构体的特征是其链指针指向NULL,因此将新插入的尾部结构体的链指针book->next指向NULL
book->next = NULL;
PRINTF("book->next set to NULL\n");
}
// 如果结构体指针(* library)的值是NULL,那么意味着新插入的结构体book既是链表的首结构体也是尾部结构体
else
{
// 链表的头指针(* library)必须指向链表的首结构体,因此将(* library)指向新插入的结构体book
* library = book;
PRINTF("struct pointer (* library) points to struct book : %p\n", * library);
// 链表的尾部结构体的特征是其链指针指向NULL,因此将新插入的尾部结构体的链指针book->next指向NULL
book->next = NULL;
PRINTF("book->next set to NULL\n");
}
// 把链表的尾结构体指针指向新插入的结构体book
tail = book;

PRINT(INFO: 完成录入\n);

return count;
}

int main(void)
{
// 定义结构体指针library,作为链表的头指针
struct Book * library = NULL;
// 定义一个整型变量count,对图书信息进行计数,此外如被赋值为-1则表示结束程序
int count = 0;

// 定义一个函数指针变量fp
int (* fp) (struct Book * *, int) = NULL;

do
{
if (ARRAY_SIZE(bookArray) <= count)
{
PRINT(INFO: 已达书籍信息存储上限,打印图书信息并结束程序\n);
count = printLibrary(&library, count);
count = releaseLibrary(&library, count);
break;
}
else
{
fp = SelectOperation(count);
count = manageLibrary(fp, &library, count);
}
} while(-1 != count);

PRINT(INFO: 程序结束);

return 0;
}
posted @   JohnnyH  阅读(149)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示