C语言笔记(五)

四十一、结构体

41.1、结构体声明

在 C 语言中,可以使用结构体(structure)来组织不同类型的数据。

结构体声明(structure declaration)是描述结构体组合的主要方法,语法如下:

struct 结构体名称
{
        结构体成员 1;
        结构体成员 2;
        结构体成员 3;
};

其中,结构体成员既可以是任何一种基本的数据类型,也可以是另一个结构体(相当于结构体的嵌套)。

注意:结构体声明你即可以放在所有函数的外面,也可以单独在一个函数里面声明。如果是后者,则该结构体只能在该函数中被定义。

41.2、定义结构体类型变量

结构体声明只是进行一个框架的描绘,它并不会在内存中分配空间存储数据,直到你定义一个结构体类型的变量。

定义结构体变量的语法如下:

struct 结构体名称 结构体变量名;

注意:这里的 struct 关键字不能丢。

41.3、访问结构体成员

要访问结构体成员,我们需要引入一个新的运算符——点号(.)运算符。

比如 book.title 就是引用 book 结构体的 title 成员;而 book.price 则是引用 book 结构体的 price 成员。

41.4、初始化结构体

可以在定义结构体变量的时候同时为其初始化:

struct Book book = {
        "《带你学C带你飞》",
        "小甲鱼",
        48.8,
        20171111,
        "清华大学出版社"
};

C99 增加了一种新特性:支持初始结构体的指定成员值。

其语法和数组指定初始化元素类似,只不过结构体指定初始化成员使用点号(.)运算符和成员名(数组则是用方括号和下标索引值)。

比如我们可以让程序只初始化 Book 的 price 成员:

struct Book book = {.price = 48.8};

利用该特性,还可以不按结构体声明的成员顺序进行初始化:

struct Book book = {
        .publisher = "清华大学出版社",
        .price = 48.8,
        .date = 20171111
};

注意:其它未初始化的数值型成员也将被自动初始化,其中数值型成员初始化为 0,字符型成员初始化为 ‘\0’。
C 结构体打包技艺 ->C 结构体打包技艺

四十二、结构体数组和结构体指针

42.1、结构体嵌套

结构体可以进行嵌套声明,比如:

struct Date
{
        int year;
        int month;
        int day;
};

struct Book
{
        char title[128];
        char author[40];
        float price;
        struct Date date;
        char publisher[40];
};

要访问其结构体成员的话,就需要使用两层点号(.)运算符来进行操作。

因此,试图用 book.date 访问日期的做法是错误的,你只能通过 book.date.year,book.date.month 和 book.date.day 依次打印出年月日。

42.2、结构体数组

结构体数组跟之前我们学习过数组概念一致,只不过是每个数组元素不再是简单的基础类型,而是一个结构体类型的数据。

定义结构体数组和定义一个结构体变量的语法类似。

第一种方法是在声明结构体的时候进行定义:

struct 结构体名称
{
        结构体成员;
} 数组名[长度];

第二种方法是先声明一个结构体类型(比如上面 Book),再用此类型定义一个结构体数组:

struct 结构体名称
{
        结构体成员;
};

struct 结构体名称 数组名[长度];

42.3、结构体指针

指向结构体变量的指针我们称之为结构体指针:

struct Book * pt;

这里声明的就是一个指向 Book 结构体类型的指针变量 pt。

我们知道数组名其实是指向这个数组第一个元素的地址,所以我们可以将数组名直接赋值给指针变量。

但注意,结构体变量不一样,结构体的变量名并不是指向该结构体的地址,所以要使用取地址运算符(&)才能获取其地址:

pt = &book;

通过结构体指针访问结构体成员有两种方法:

  • (*结构体指针).成员名
  • 结构体指针->成员名
    第一种方法由于点号运算符(.)比指针的取值运算符(*)优先级要高,所以要使用小括号先对指针进行解引用,让它先变成该结构体变量,再用点运算符去访问其成员。

相比之下,第二种方法更加方便和直观。不知道大家有没有发现,第二种方法使用的成员选择运算符(->)自身的形状就是一个箭头,箭头具有指向性,所以我们一下子就把它跟指针联系起来。

需要注意的是,两种方法在实现上是完全等价的,所以无论你习惯使用哪一种方法都可以访问到结构体的成员。

但切记,点号(.)只能用于结构体,而箭头(->)只能用于结构体指针,这两个就不能混淆。

四十三、传递结构体变量和结构体指针

43.1、传递结构体变量

两个相同结构体类型的变量可以直接进行赋值:

...
        struct Test
        {
                int x;
                int y;
        }t1, t2;

        t1.x = 3;
        t1.y = 4;

        t2 = t1;
...

可以看到 t2 = t1; 语句将 t1 这个结构体变量所有成员的值都成功地赋值给了 t2。
那么同样的道理,结构体变量也是可以作为函数参数进行传递的:

#include <stdio.h>

struct Date
{
        int year;
        int month;
        int day;
};

struct Book
{
        char title[128];
        char author[40];
        float price;
        struct Date date;  
        char publisher[40];
};

struct Book getInput(struct Book book);
void printBook(struct Book book);

struct Book getInput(struct Book book)
{
        printf("请输入书名:");
        scanf("%s", book.title);
        printf("请输入作者:");
        scanf("%s", book.author);
        printf("请输入售价:");
        scanf("%f", &book.price);
        printf("请输入出版日期:");
        scanf("%d-%d-%d", &book.date.year, &book.date.month, &book.date.day);
        printf("请输入出版社:");
        scanf("%s", book.publisher);

        return book;
}

void printBook(struct Book book)
{
        printf("书名:%s\n", book.title);
        printf("作者:%s\n", book.author);
        printf("售价:%.2f\n", book.price);
        printf("出版日期:%d-%d-%d\n", book.date.year, book.date.month, book.date.day);
        printf("出版社:%s\n", book.publisher);
}

int main(void)
{
        struct Book b1, b2;

        printf("请录入第一本书的信息...\n");
        b1 = getInput(b1);
        putchar('\n');
        printf("请录入第二本书的信息...\n");
        b2 = getInput(b2);

        printf("\n\n录入完毕,现在开始打印验证...\n\n");
        printf("打印第一本书的信息...\n");
        printBook(b1);
        putchar('\n');
        printf("打印第二本书的信息...\n");
        printBook(b2);

        return 0;
}

43.2、传递指向结构体变量的指针

在最开始的时候,C语言是不允许直接将结构体作为参数传递给函数的。

当初有这么一个限制主要是出于对程序执行效率上的考虑。

因为结构体变量的尺寸可以是很大的,那么在函数调用的过程中将会导致空间和时间上的开销也相对是巨大的。

既然传递结构体变量可能导致程序的开销变大,那么应该如何做才好呢?

没错,使用万能的指针!

#include <stdio.h>

struct Date
{
        int year;
        int month;
        int day;
};

struct Book
{
        char title[128];
        char author[40];
        float price;
        struct Date date;  
        char publisher[40];
};

struct Book getInput(struct Book book);
void printBook(struct Book book);

void getInput(struct Book *book)
{
        printf("请输入书名:");
        scanf("%s", book->title);
        printf("请输入作者:");
        scanf("%s", book->author);
        printf("请输入售价:");
        scanf("%f", &book->price);
        printf("请输入出版日期:");
        scanf("%d-%d-%d", &book->date.year, &book->date.month, &book->date.day);
        printf("请输入出版社:");
        scanf("%s", book->publisher);
}

void printBook(struct Book *book)
{
        printf("书名:%s\n", book->title);
        printf("作者:%s\n", book->author);
        printf("售价:%.2f\n", book->price);
        printf("出版日期:%d-%d-%d\n", book->date.year, book->date.month, book->date.day);
        printf("出版社:%s\n", book->publisher);
}

int main(void)
{
        struct Book b1, b2;

        printf("请录入第一本书的信息...\n");
        getInput(&b1);
        putchar('\n');
        printf("请录入第二本书的信息...\n");
        getInput(&b2);

        printf("\n\n录入完毕,现在开始打印验证...\n\n");
        printf("打印第一本书的信息...\n");
        printBook(&b1);
        putchar('\n');
        printf("打印第二本书的信息...\n");
        printBook(&b2);

        return 0;
}

这次我们传递过去的就是一个指针,而不是整个庞大的结构体。

注意:这里由于传进来的实参是一个指针,所以要使用箭头(->)来访问结构体变量的成员。

43.3、动态申请结构体

还可以动态地在堆里面给结构体分配存储空间:

int main(void)
{
        struct Book *b1, *b2;

        b1 = (struct Book *)malloc(sizeof(struct Book));
        b2 = (struct Book *)malloc(sizeof(struct Book));
        if (b1 == NULL || b2 == NULL)
        {
                printf("内存分配失败!\n");
                exit(1);
        }

        printf("请录入第一本书的信息...\n");
        getInput(b1);
        putchar('\n');
        printf("请录入第二本书的信息...\n");
        getInput(b2);

        printf("\n\n录入完毕,现在开始打印验证...\n\n"); 
        printf("打印第一本书的信息...\n");
        printBook(b1);
        putchar('\n');
        printf("打印第二本书的信息...\n");
        printBook(b2);

        free(b1);
        free(b2);

        return 0;
}

43.4、构建图书馆

现在要求大家来构建一个图书馆,然后让用户将书籍的信息都录入到里面。

提示:“图书馆” 其实就是存放 Book 结构体变量的指针数组,每个数组元素存放的是指向一个动态申请的 Book 结构体变量的指针。

关系大概是这样子:
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

struct Date
{
        int year;
        int month;
        int day;
};

struct Book
{
        char title[128];
        char author[40];
        float price;
        struct Date date;  
        char publisher[40];
};

void getInput(struct Book *book);
void printBook(struct Book *book);
void initLibrary(struct Book *library[]);
void printLibrary(struct Book *library[]);
void releaseLibrary(struct Book *library[]);

void getInput(struct Book *book)
{
        printf("请输入书名:");
        scanf("%s", book->title);
        printf("请输入作者:");
        scanf("%s", book->author);
        printf("请输入售价:");
        scanf("%f", &book->price);
        printf("请输入出版日期:");
        scanf("%d-%d-%d", &book->date.year, &book->date.month, &book->date.day);
        printf("请输入出版社:");
        scanf("%s", book->publisher);
}

void printBook(struct Book *book)
{
        printf("书名:%s\n", book->title);
        printf("作者:%s\n", book->author);
        printf("售价:%.2f\n", book->price);
        printf("出版日期:%d-%d-%d\n", book->date.year, book->date.month, book->date.day);
        printf("出版社:%s\n", book->publisher);
}

void initLibrary(struct Book *library[])
{
        int i;

        for (i = 0; i < MAX_SIZE; i++)
        {
                library[i] = NULL;
        }
}

void printLibrary(struct Book *library[])
{
        int i;

        for (i = 0; i < MAX_SIZE; i++)
        {
                if (library[i] != NULL)
                {
                        printBook(library[i]);
                        putchar('\n');
                }
        }

}

void releaseLibrary(struct Book *library[])
{
        int i;

        for (i = 0; i < MAX_SIZE; i++)
        {
                if (library[i] != NULL)
                {
                        free(library[i]);
                }
        }
}

int main(void)
{
        struct Book *library[MAX_SIZE];
        struct Book *ptr = NULL;
        int ch, index = 0;

        initLibrary(library);

        while (1)
        {
                printf("请问是否需要录入图书信息(Y/N):");
                do
                {
                        ch = getchar();
                } while (ch != 'Y' && ch != 'N');

                if (ch == 'Y')
                {
                        if (index < MAX_SIZE)
                        {
                                ptr = (struct Book *)malloc(sizeof(struct Book));
                                getInput(ptr);
                                library[index] = ptr;
                                index++;
                                putchar('\n');
                        }
                        else
                        {
                                printf("该图书馆已满,无法录入新数据!\n");
                                break;
                        }
                }
                else
                {
                        break;
                }
        }

        printf("\n\n录入完毕,现在开始打印验证...\n\n");

        printLibrary(library);

        releaseLibrary(library);

        return 0;
}

四十四、单链表

44.1、 声明一个指向自身的结构体

不能这样写:

struct Test
{
        int x;
        int y;
        struct Test test;
};

要这样写:

struct Test
{
        int x;
        int y;
        struct Test *test;
};

44.2、单链表

单链表是最简单的一种链表实现方式,它包含两个域,一个信息域和一个指针域:
在这里插入图片描述
真正的单链表它还需要一个头指针,用于存放指向链表第一个节点的地址(这样你才能知道到它):
在这里插入图片描述
对于 Book 结构体来说,要把它变成链表的其中一个元素,我们只需要为其添加一个指向自身的成员即可:

...
struct Book
{
        char title[128];
        char author[40];
        float price;
        struct Date date;
        char publisher[40];
        struct Book *next;
};
...

44.3、在单链表中插入元素(头插法)

在单链表中插入元素,事实上只需要修改指针的指向即可:
在这里插入图片描述
将书籍添加到单链表的代码我们这么可以写:

...
void addBook(struct Book **library)
{
        struct Book *book, *temp;

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

        getInput(book);

        if (*library != NULL)
        {
                temp = *library;
                *library = book;
                book->next = temp;
        }
        else
        {
                *library = book;
                book->next = NULL;
        }
}
...

四十五、单链表2

45.1、尾插法

上一节课我们演示了单链表的头插法,就是将数据插入到单链表的头部位置。

那么相对应的还有另一个种方法:尾插法 —— 将数据插入到单链表的尾部位置。
在这里插入图片描述

...
void addBook(struct Book **library)
{
        struct Book *book;
        static struct Book *tail;

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

        getInput(book);

        if (*library != NULL)
        {
                tail->next = book;
                book->next = NULL;
        }
        else
        {
                *library = book;
                book->next = NULL;
        }

        tail = book;
}
...

45.2、搜索单链表

有时候我们可能会对单链表进行搜索操作,比如输入这个书名或者作者的名字,可以找到相关的节点数据。

struct Book *searchBook(struct Book *library, char *target)
{
        struct Book *book;

        book = library;
        while (book != NULL)
        {
                if (!strcmp(book->title, target) || !strcmp(book->author, target))
                {
                        break;
                }
                book = book->next;
        }

        return book;
}

void printBook(struct Book *book)
{
        printf("书名:%s\n", book->title);
        printf("作者:%s\n", book->author);
}
...
int main(void)
{
        ...
        printf("\n请输入书名或作者:");
        scanf("%s", input);

        book = searchBook(library, input);
        if (book == NULL)
        {
                printf("很抱歉,没能找到!\n");
        }
        else
        {
                do
                {
                        printf("已找到符合条件的书籍...\n");
                        printBook(book);
                } while ((book = searchBook(book->next, input)) != NULL);
        }

        releaseLibrary(&library);

        return 0;
}

四十六、单链表3

46.1、插入节点到指定的位置

我们之前说单链表和数组相比较的话,最大的优势就是插入元素到指定位置的效率。

对于数组来说,插入一个元素到指定的位置,需要将其后面所有的元素都挨个儿移动一次,效率之低下可想而知。

相比之下,单链表的效率就要高很多了。因为对于单链表来说,只需要轻轻地改动一下指针即可。
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>

struct Node
{
        int value;
        struct Node *next;
};

void insertNode(struct Node **head, int value)
{
        struct Node *previous;
        struct Node *current;
        struct Node *new;

        current = *head;
        previous = NULL;

        while (current != NULL && current->value < value)
        {
                previous = current;
                current = current->next;
        }

        new = (struct Node *)malloc(sizeof(struct Node));
        if (new == NULL)
        {
                printf("内存分配失败!\n");
                exit(1);
        }
        new->value = value;
        new->next = current;

        if (previous == NULL)
        {
                *head = new;
        }
        else
        {
                previous->next = new;
        }
}

void printNode(struct Node *head)
{
        struct Node *current;

        current = head;
        while (current != NULL)
        {
                printf("%d ", current->value);
                current = current->next;
        }

        putchar('\n');
}

int main(void)
{
        struct Node *head = NULL;
        int input;

        printf("开始测试插入整数...\n");
        while (1)
        {
                printf("请输入一个整数(输入-1表示结束):");
                scanf("%d", &input);
                if (input == -1)
                {
                        break;
                }
                insertNode(&head, input);
                printNode(head);
        }
        
        return 0;
}

程序实现如下:

[fishc@localhost s1e47]$ gcc test1.c && ./a.out
开始测试插入整数...
请输入一个整数(输入-1表示结束)5
5 
请输入一个整数(输入-1表示结束)3
3 5 
请输入一个整数(输入-1表示结束)8
3 5 8 
请输入一个整数(输入-1表示结束)9
3 5 8 9 
请输入一个整数(输入-1表示结束)1
1 3 5 8 9 
请输入一个整数(输入-1表示结束)0
0 1 3 5 8 9 
请输入一个整数(输入-1表示结束)2
0 1 2 3 5 8 9 
请输入一个整数(输入-1表示结束)4
0 1 2 3 4 5 8 9 
请输入一个整数(输入-1表示结束)7
0 1 2 3 4 5 7 8 9 
请输入一个整数(输入-1表示结束)-1

我们重点分析一下这个 insertNode 函数:

while (current != NULL && current->value < value)
{
        previous = current;
        current = current->next;
}

while 函数用于找到符合条件的链表节点,也就是在有序的链表中找到比传入的value更大的值,然后停下来;如果没有,则在链表的尾部位置停止(current == NULL 时结束循环)。

由于单链表一旦指向下一个节点,就没办法回头了,所以我们使用 previous 变量来记录 current 节点的上一个节点。
在这里插入图片描述
最后判断一下 previous 变量,如果为 NULL 的话,说明 while 循环它压根儿就没进去过,有两种情况,要么是这是一个空链表(current == NULL),或者该值比当前链表中所有的节点的 value 成员都小,无论是哪一种情况,我们都将该值插入为单链表的第一个节点即可。

46.2、在单链表中删除元素

我们的单链表应该支持删除某一个节点的数据。

删除某个节点的数据,其实也是修改指针的事儿,两个步骤搞定:

  • 修改待删除节点的上一个节点的指针,将其指向待删除节点的下一个节点
  • 释放待删除节点的内存空间
    在这里插入图片描述
...
void deleteNode(struct Node **head, int value)
{
        struct Node *previous;
        struct Node *current;

        current = *head;
        previous = NULL;

        while (current != NULL && current->value != value)
        {
                previous = current;
                current = current->next;
        }

        if (current == NULL)
        {
                printf("找不到匹配的节点!\n");
                return ;
        }
        else
        {
                if (previous == NULL)
                {
                        *head = current->next;
                }
                else
                {
                        previous->next = current->next;
                }
                free(current);
        }
}
...
[fishc@localhost s1e47]$ gcc test2.c && ./a.out
开始测试插入整数...
请输入一个整数(输入-1表示结束)5
5 
请输入一个整数(输入-1表示结束)3
3 5 
请输入一个整数(输入-1表示结束)1
1 3 5 
请输入一个整数(输入-1表示结束)9
1 3 5 9 
请输入一个整数(输入-1表示结束)8 
1 3 5 8 9 
请输入一个整数(输入-1表示结束)7
1 3 5 7 8 9 
请输入一个整数(输入-1表示结束)0
0 1 3 5 7 8 9 
请输入一个整数(输入-1表示结束)-1
开始测试删除整数...
请输入一个整数(输入-1表示结束)0
1 3 5 7 8 9 
请输入一个整数(输入-1表示结束)9
1 3 5 7 8 
请输入一个整数(输入-1表示结束)7
1 3 5 8 
请输入一个整数(输入-1表示结束)8
1 3 5 
请输入一个整数(输入-1表示结束)-1

简单分析一下实现代码,当 current 指向 NULL 的情况有两种,要么这是一个空链表,要么在单链表的所有节点的 value 成员中找不到对应的数值,所以统一跟用户说找不到即可。

如果 current 不为 NULL,我们还要预防 previous 是否为 NULL,有一种情况会导致这种局面的发生,那就是当要删除的节点是单链表的第一个节点的时候,在这种情况下需要特殊处理:要将 head 指针指向该节点。

这个链表的知识,说白了就是考核你对结构体和指针的理解程度。

四十七、内存池

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

47.1、概念

内存池其实就是让程序额外维护一个缓存区域
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 使用单链表来维护一个简单的内存池
  • 只需要将没有用的内存空间地址依次用一个单链表记录下来;当再次需要的时候,从这个单链表中获取即可

47.2、通讯录程序(加入内存池)

// 加入内存池的版本 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX 1024        //定义内存池最大空间 

struct Person
{
        char name[40];
        char phone[20];
        struct Person *next;
}; 

struct Person *pool = NULL;
int count;

void getInput(struct Person *person);
void printPerson(struct Person *person);
void addPerson(struct Person **contacts);
void changePerson(struct Person *contacts);
void delPerson(struct Person **contacts);
struct Person *findPerson(struct Person *contacts);
void displayContacts(struct Person *contacts);
void releaseContacts(struct Person **contacts);
void releasePool(void);

void getInput(struct Person *person)
{
        printf("请输入姓名:");
        scanf("%s", person->name);
        printf("请输入电话号码:");
        scanf("%s", person->phone);
}

void addPerson(struct Person **contacts)
{
        struct Person *person;
        struct Person *temp;
        
        // 如果内存池非空,则直接从里面获取空间
        if (pool != NULL)
        {
                person = pool;
                pool = pool->next;
                count--;
        } 
        // 如果内存池为空,则调用malloc函数申请新的内存空间
        else
        {
                person = (struct Person *)malloc(sizeof(struct Person));
                if (person == NULL)
                {
                        printf("内存分配失败!\n");
                        exit(1);
                }
        } 

        getInput(person);
        
        // 将person用头插法添加到通讯录中
        if (*contacts != NULL)
        {
                temp = *contacts;
                *contacts = person;
                person->next = temp;
        }
        else
        {
                *contacts = person;
                person->next = NULL;
        }
}

void printPerson(struct Person *person)
{
        printf("联系人:%s\n", person->name);
        printf("电话:%s\n", person->phone);
}

struct Person *findPerson(struct Person *contacts)
{
        struct Person *current;
        char input[40];
        
        printf("请输入联系人:");
        scanf("%s", input);
        
        current = contacts;
        while (current != NULL && strcmp(current->name, input))
        {
                current = current->next;
        }
        
        return current;
}

void changePerson(struct Person *contacts)
{
        struct Person *person;
        
        person = findPerson(contacts);
        if (person == NULL)
        {
                printf("找不到该联系人!\n");
        }
        else
        {
                printf("请输入新的联系电话:");
                scanf("%s", person->phone);
        }
}

void delPerson(struct Person **contacts)
{
        struct Person *temp;
        struct Person *person;
        struct Person *current;
        struct Person *previous;
        
        // 先找到待删除的节点指针
        person = findPerson(*contacts);
        if (person == NULL)
        {
                printf("找不到该联系人!\n");
        }
        else
        {
                current = *contacts;
                previous = NULL;
                
                // current定位到待删除的节点
                while (current != NULL && current != person)
                {
                        previous = current;
                        current = current->next;
                }
                
                if (previous == NULL)
                {
                        // 待删除的节点是第一个节点
                        *contacts = current->next; 
                }
                else
                {
                        // 待删除的节点不是第一个节点
                        previous->next = current->next; 
                }
                
                // 判断内存池是不是有空位(头插法)
                if (count < MAX)
                {
                        if (pool != NULL)
                        {
                                temp = pool;
                                pool = person;
                                person->next = temp;
                        }
                        else
                        {
                                pool = person;
                                person->next = NULL;
                        }
                        count++;
                }
                else
                {
                        free(person);
                }
                
        }
}

void displayContacts(struct Person *contacts)
{
        struct Person *current;
        
        current = contacts;
        while (current != NULL)
        {
                printPerson(current);
                current = current->next;
        }
}

void releaseContacts(struct Person **contacts)
{
        struct Person *temp;
        
        while (*contacts != NULL)
        {
                temp = *contacts;
                *contacts = (*contacts)->next;
                free(temp);
        }
}

void releasePool(void)
{
        struct Person *temp;
        
        while (pool != NULL)
        {
                temp = pool;
                pool = pool->next;
                free(temp);
        }
}

int main(void)
{
        int code;
        struct Person *contacts = NULL;
        struct Person *person;
        
        printf("| 欢迎使用通讯录管理程序 |\n");
        printf("|--- 1:插入新的联系人 ---|\n");
        printf("|--- 2:查找已有联系人 ---|\n");
        printf("|--- 3:更改已有联系人 ---|\n");
        printf("|--- 4:删除已有联系人 ---|\n");
        printf("|--- 5:显示当前通讯录 ---|\n");
        printf("|--- 6:退出通讯录程序 ---|\n");
        printf("|- Powered by FishC.com -|\n");
        
        while (1)
        {
                printf("\n请输入指令代码:");
                scanf("%d", &code);
                switch (code)
                {
                        case 1: addPerson(&contacts); break;
                        
                        case 2: person = findPerson(contacts);
                                        if (person == NULL)
                                        {
                                                printf("找不到联系人!\n"); 
                                        }
                                        else
                                        {
                                                printPerson(person);
                                        }
                                        break;
                        
                        case 3: changePerson(contacts); break;
                        
                        case 4: delPerson(&contacts); break;
                        
                        case 5: displayContacts(contacts); break;
                        
                        case 6: goto END;
                }
        }
        
END:
        releaseContacts(&contacts);
        releasePool();
        return 0;
}

四十八、基础typedef

48.1、typedef

typedef是c语言最重要的关键字之一
在这里插入图片描述

48.2、define

在这里插入图片描述

48.3、typedef和define区别

define只是字面上的替换
在这里插入图片描述
在这里插入图片描述
为什么a会变成这样,可以看-》进制转换使用补码的好处
在这里插入图片描述在这里插入图片描述
使用typedef,b和c都是指针,使用define,b是指针,而c是int,因为define只是机械的替换
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 相比起宏定义的直接替换,typedef是对类型的封装

48.4、typedef的用法之一

在这里插入图片描述

四十九、进阶typedef

49.1、使用typedef目的

  • 给变量起一个容易记住且意义明确的别名
  • 简化一些比较复杂的类型声明

49.2、进阶typedef语句

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五十、共用体

50.1、定义

  • 允许您在相同的内存位置存储不同的数据类型,共用体

  • 可以定义一个带有多成员的共用体

  • 但是任何时候只能有一个成员带有值

  • 共用体提供了一种使用相同的内存位置的有效方式

  • 必须使用 union 语句

  • union 语句定义了一个新的数据类型,带有多个成员

  • 在共用体定义的末尾,最后一个分号之前,您可以指定一个或多个共用体变量,这是可选的

  • 共用体占用的内存应足够存储共用体中最大的成员

union [union tag]
{
   member definition;
   member definition;
   ...
   member definition;
} [one or more union variables];

在这里插入图片描述

50.2、定义共用体类型变量

  • 定义共用体跟定义结构体的语法相似,你可以先声明一个共用体类型,再定义共用体变量
union data
{
	int i;
	char ch;
	float f;
};
union data a,b,c;
  • 也可以在声明的同时定义共用体变量
union data
{
	int i;
	char ch;
	float f;
}a,b,c;

50.3、初始化共用体

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值