laiyuou 发表于 2021-11-12 14:00

学习c语言的第五天:结构指针变量的说明和简单使用以及简单链表的认识

**结构指针变量的说明和使用:**
一个指针变量当用来指向一个结构变量时,称之为结构指针变量。结构指针变量中的值是所指向的结构变量的首地址。
通过结构指针可访问该结构变量,这与数组指针和函数指针的情况是相同的
结构指针变量的说明的一般形式:
struct 结构名 *结构指针变量名
例如:
struct stu *pstu;
结构指针变量是要先赋值后才能使用。赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。
结构指针变量的一般访问形式:
(*结构指针变量).成员名
或者:结构指针变量->成员名

例子1:
```
struct stu {
        int num;
        char *name;
        char sex;
        float score;
}boy1 = {102,"Zhang ping",'M',78.5},*pstu;

int main()
{
        pstu = &boy1;//把结构变量的boy1的地址赋值给pstu
        printf("Number=%d\nName=%s\n", boy1.num, boy1.name);
        printf("Sex=%c\nScore=%f\n\n", boy1.sex, boy1.score);
        printf("Number=%d\nName=%s\n", (*pstu).num, (*pstu).name);
        printf("Sex=%c\nScore%f\n\n", (*pstu).sex, (*pstu).score);
        printf("Number=%d\nName=%s\n", pstu->num, pstu->name);
        printf("Sex=%c\nScore=%f", pstu->sex, pstu->score);
        return 0;
}
```
结构指针变量作函数参数
在ANSI的c标准中,是允许使用结构变量作为函数参数进行整体传送的。但是这种传送要将全部成员逐个传送,但是会花费很
的时间和空间。因此使用指针来进行传送更好

例子2:计算一组学生的平均成绩和不及格人数。使用结构指针变量作函数参数编程
```
struct stu
{
        int num;
        char *name;
        char sex;
        float score;
}boy = {
        {101,"Li ping",'M',45},
{102,"Zhang ping",'M',62.5},
{103,"He feng",'F',92.5},
{104,"Cheng ling",'F',87},
{105,"Wang ming",'M',58}
};
int main()
{
        struct stu *ps;
        void ave(struct stu *ps);
        ps = boy;
        ave(ps);
        return 0;
}
void ave(struct stu *ps)
{
        int c = 0;
        float ave = 0,s=0;
        for (int i = 0; i < 5; i++, ps++)
        {
                s += ps->score;
                if ((ps->score) < 60)
                {
                        c += 1;
                }
        }
        printf("s=%f\n", s);
        ave = s / 5;
        printf("average=%f\ncount=%d\n", ave, c);
}
```


动态存储分配;
在c语言中不允许动态数组类型
c语言提供了一些内存管理函数,主要有三种:

分配内存空间函数malloc:
调用的形式:(类型说明符*)malloc(size)
功能:在内存的动态存储区中分配一块长度为“size”字节的连续区域。函数的返回值为该区域的首地址。
“类型说明符”表示把该区域用于何种数据类型。
(类型说明符*)表示把返回值强制转换为该类型指针。

分配内存空间函数:calloc
调用形式:(类型说明符*)calloc(n,size)
功能:在内存动态存储区中分配n块长度为:“size”字节的连续区域。函数的返回值是为该函数的首地址。
(类型说明符*)用于强制类型转换。
calloc函数与malloc函数的去区别仅在于一次可以分配n块区域。
例如:ps=(struct stu*)calloc(2,sizeof(struct stu);-->其中siezeof(struct stu)是求stu的结构长度。因此该语句的意思:
按照stu的长度分配2块连续区域,强制转换为stu类型,并把其首地址赋予指针变量ps.

释放内存空间函数free
调用形式:free(void *ptr):释放ptr所指向的一块内存空间,ptu是一个任意类型的指针变量,它指向被释放区域的首地址。被释放区
应是由malloc或者calloc函数所分配的区域。
例子3:分配一个块区域,输入一个学生数据
```
int main()
{
        struct stu
        {
                int num;
                char *name;
                char sex;
                float score;
        }*ps;//ps这个指针变量指向一个stu结构的首地址
        ps = (struct stu*)malloc(sizeof(struct stu));
        ps->num = 102;
        ps->name = "Zhang ping";
        ps->sex = 'M';
        ps->score = 62.5;
        printf("Num=%d\nName=%s\n", ps->num, ps->name);
        printf("Sex=%c\nScore=%f\n", ps->sex, ps->score);
        free(ps);
        return 0;
}

```
        链表的概念:即就是动态存储的办法,我们来存放一个学生的数据是,可以分配一块可用空间来存放,我也称之这样一块存储的空间为
一个节点,我们存放一个学生数据就分配一个节点,无须预先确定学生的准确人数,某学生退学,可以删去该节点,并释放该节点占用的空间
使用动态分配时,每个节点之间可以是不连续(结点内部之间是连续的),而结点之间的连续可以用指针来实现。即
在结点结构中定义一个成员项用来存放下一个结点的首地址,这个用于存放地址的成员,常把它称为指针域。
我们可以在结点的指针域中存放第二个结点的首地址,在第二个结点的指针域内与存放第三个结点的首地址,如此串联下去直到最后一个结点
最后一个结点因无后续结点连接,其指针域可赋值为0.这样的一种连接方式,在数据结构中称为:“链表”。
例如:一个存放学生学号和成绩的结点应为一下结构:
```
struct stu
{
        int num;
        int score;
        struct stu *next;
};//前面两个成员组成数据域,后一个成员项next构成指针域,他是一个指向stu类型结构的指针变量。
```
链表的基本操作对链表的主要操作有一下几种:
1.建立链表;
2.结构的查找与输出;
3.插入一个结点;
4.删除一个结点;

例子4:建立一个三个结点的链表,存放学生的数据。为了简单起见,我们假定学生数据结构中只有学号和年龄两项。可编写一个建立链表的函数creat。
```
#define NULL 0
#define TYPE struct stu
#define LEN sizeof(struct stu)

struct stu
{
        int num;
        int age;
        struct stu *next;
};
TYPE *creat(int n)
{
        struct stu *head, *pf, *pb;
        int i;
        for (i = 0; i < n; i++)
        {
                pb = (TYPE*)malloc(LEN);
                printf("input Number and Age:\n");
                scanf_s("%d%d", &pb->num, &pb->age);
                if (i == 0)
                {
                        pf = head = pb;
                }
                else
                {
                        pf->next = pb;
                        pb->next = NULL;
                        pf = pb;
                }
                return (head);
        }
}
```
在函数外首先用宏定义对三个符号常量作了定义。这样用TYPE表示struct stu,用LEN表示sizeof(struct stu)主要的目的是为了
在一下程序内减少书写并使阅读更加方便。结构stu定义为外部类型,程序中的各个函数均可使用该定义。
creat函数用于建立一个有n 个结点的链表,它是一个指针函数,他返回的指针指向stu结构。在creat函数内定义了三个stu结构的指针变量
head为头指针,pf为指向两相邻结点的前一结点的指针变量,bp为后指针变量

创建一个有n个结点的链表

创建链表:
```
typedef struct student {
        int score;
        struct student *next;
}LinkList;
```
一般我们创建链表都用typedef struct,因为这样定义结构体变量时,我们就可以直接使用LinkList,*a定义的结构体类型变量了。
初始化一个链表,n为链表结点个数。
```
LinkList *creat(int n)
{
        LinkList *head,*node ,*end;
        head = (LinkList*)malloc(sizeof(LinkList));//为头结点分配内存空间
        end = head;
        for (int i = 0; i < n; i++)
        {
                node = (LinkList*)malloc(sizeof(LinkList));//为不同结点分配内存空间
                scanf_s("%d\n", &node->score);
                end->next = node;//把当前结点的地址给到下一个结构体结点end的指针域
                end = node;
        }
        end->next = NULL;
        return head;
}

```
修改链表的结点值:
```
void change(LinkList *list, int n)
{
        LinkList *t = list;
        int i = 0;
        while (i < n && t!= NULL)
        {
                t = t->next;//通过这一步跳到下一个节点
                i++;
        }
        if (t != NULL)
        {
                puts("输入要修改的值\n");
                scanf_s("%d\n", &t->score);
        }
        else
        {
                puts("节点不存在");
        }
}
```

删除链表结点:也就是把前节点的指针域越过要删除的节点指向下下一个节点。
```
void delet(LinkList *list, int n)
{
        LinkList *t = list, *in;
        int i = 0;
        while (i < n&& t != NULL)
        {
                in = t;
                t = t->next;//通过这一步跳到下一个节点
                i++;
        }
        if (t != NULL)
        {
                in->next = t->next;
                free(t);
        }
        else
        {
                puts("结点不存在");
        }
}
```
插入链表节点:就是把插入前的head指针域即head->next赋值给想要插入的这个结点的指针域(假设插入的这个节点为e,即e->next = head->next)
然后再把插入的结点的首地址赋值给在head的指针域:head->next = e;
```
void insert(LinkList *list, int n)
{
        LinkList *t = list, *in;
        int i = 0;
        while (i < n && t != NULL)
        {
                t = t->next;
                i++;
        }
        if (t != NULL)
        {
                in = (LinkList*)malloc(sizeof(LinkList));
                puts("请输入要插入的值");
                scanf_s("%d", &in->score);
                in->next = t->next;
                t->next = in;
        }
        else
        {
                puts("节点不存在");
        }
}
```
输出链表:通过遍历输出链表
```
while (h->next != NULL)
{
        h = h->next;
        printf("%d\n", h->score);
}

```

偶尔平凡 发表于 2021-11-12 14:12

lsjhwei 发表于 2021-11-12 15:19

谢谢分享

15071719559 发表于 2021-11-12 15:32

这是五天的结果吗?太强了吧

m350 发表于 2021-11-12 16:09

用的什么软件能分享下吗{:1_893:}

okxiaobu 发表于 2021-11-12 16:23

跟着楼主学习,以前学的,不写代码都忘了,跟着复习下

bester 发表于 2021-11-12 17:20

你好 请问你看的是谁的视频教程呢 能不能推荐一下

laiyuou 发表于 2021-11-12 17:28

m350 发表于 2021-11-12 16:09
用的什么软件能分享下吗

vs2017,好像是企业版

laiyuou 发表于 2021-11-12 17:29

bester 发表于 2021-11-12 17:20
你好 请问你看的是谁的视频教程呢 能不能推荐一下

c语言程序设计谭浩强第三版+csdn+b站{:1_924:}
页: [1]
查看完整版本: 学习c语言的第五天:结构指针变量的说明和简单使用以及简单链表的认识