C语言的结构体(指针),链表

结构体来了!

已经呕心沥血了!!!!!!!

结构体

一般形式

struct 结构名

{        

        成员列表
}变量名列表;//结构体的最后有一个分号!!!

成员名的各个数据类型也必须声明

嵌套定义

注意在我输入嵌套结构体时各个变量之间成员运算符是英文符号的   (.)   而不是  (,)

它在所有的运算符中优先级最高所以可以把其作为一个整体来看待

结构体变量的引用

注意,输出的结果SCORE为0.000000,是因为它吸收了一个回车所以自动默认为零

结构体的地址等于结构体数个成分变量的地址

结构体的初始化

结构体与数组以及引用和定义

初始化

当设置一个指针变量用来指向一个结构体变量时,该指针变量的值就是结构体变量的起始地址,指针就是用来存储地址的变量,通过它可以方便地访问和操作所指向的结构体数据。这样就可以通过指针来间接访问结构体中的各个成员。

一些知识点如下图

其访问的一般形式为:
(*结构体指针变量).成员名
或为:
结构指针变量 -> 成员名
例如:
(*pstu).num
或者
pstu ->num

结构体指针变量作函数参数

将一个结构体变量的值传递给另一个函数,有3个方法
1.用结构体变量的成员作参数
2.用结构体变量作实参
3.用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参。

动态存储分配

在数组一章里,介绍过数组的长度是预先定义好的,在整个程序中固定不变,C语言中不允许动态数组类型。
例如:
int a[n];
用变量表示长度,想对数组的大小作动态说明,这是错误的。
但是在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。

动态存储方式在C语言中指的是系统在程序运行时根据需要动态地分配存储空间。这种存储方式与静态存储方式不同,后者是在程序编译时就已经为变量分配了固定的存储空间。动态存储方式主要涉及到以下几个方面:
动态存储区:这是内存中专门用于动态分配存储空间的地方,主要包括堆区(heap)。程序员可以根据需要在这里分配和释放内存。
动态分配函数:C语言提供了几个标准库函数用于在堆区分配内存,包括 malloc 、 calloc 、 realloc 和 free 。这些函数允许程序在运行时动态地请求内存空间,从而增加了程序设计的灵活性。
 malloc 函数用于分配指定大小的内存块,并返回所分配内存块的首地址。
 calloc 函数可以分配多个大小相同的内存块,这些内存块的内容会被初始化为0。
 realloc 函数用于重新调整之前分配的内存块的大小。
 free 函数用于释放先前分配的内存块,使这段内存重新可用。
动态存储的应用场景:动态存储方式特别适合于那些在编写程序时无法确定所需内存大小的场合,例如处理不同大小的数据结构(如链表、树等),或者是在程序运行时需要动态创建的数据对象。
注意事项:虽然动态存储提供了灵活性,但也需要程序员小心管理。内存泄漏(分配了内存但未释放)和悬挂指针(已经释放内存但仍然使用)是常见的错误,可能导致程序运行不稳定或崩溃。
动态存储方式是C语言中管理内存的一种重要手段,合理使用可以大大提高程序的灵活性和效率。

链表

链表是一种常见的重要的数据结构,是动态地进行存储分配的一种结构。链表的组成:头指针:存放一个地址,该地址指向第一个元素。结点:用户需要的实际数据和链接节点的指针

具体例子

静态链表

基本的注意事项和解释在途中都有,大家可以看一下.

动态链表

所谓动态链表是指程序在执行过程中从无到有建起一个列表,即一个一个地开辟结点和输入个节点的数据,并建立起前后相链的关系

例题: 一个程序建立一个含有学生(学号,成绩)数据的单向动态链表。
约定: 学号不会为0,如果输入的学号为0,表示链表建立过程完成,该结点不会连接到链表中

再开辟一个结点并使p1指向它,接着输入该结点的数据。
如果输入的p1->num 不等于0,则应链入 第二个结点(n=2),将新的结点的地址赋给第个结点的next成员。
接着 使 p2 = p1,也就是使p2指向刚才新建的结点

再开辟一个结点并使p1指向它,并输入该结点的数据。
在第三次循环中,由于n=3(n不等于1),又将p1的值赋给p2->next,也就是将第三个结链接到第二个结点之后,并使p2=p1,使p2指向最后一个结点。

再开辟一个新结点,并使p1指向它,输入该结点的数据,由于p1->num 的值为0,不再执行循环,此新节点不应被链接到链表中:
将NULL赋给p2->next;
建立链表过程至此结束,p1最后所指的结点并未链入链表中,第三个结点的next成员的指为NULL,它不指向任何结点。

实现对链表的打印
完整的代码演示

代码解释

  1. 结构体定义
    • struct student 定义了一个学生结构体,包含学号(num)、成绩(score)和指向下一个学生的指针(next)。
  2. 全局变量
    • n 用于记录链表中存储的学生数量。
  3. 函数
    • creat():创建一个链表,用户输入学生的学号和成绩,直到输入学号为0时停止输入。
    • print():打印链表中的所有学生信息。
  4. main() 函数
    • 创建一个学生链表,并打印链表内容。

 

对链表的删除

#include <stdio.h>  
#include <stdlib.h>  
  
#define LEN sizeof(struct student) // 定义学生结构体的大小  
  
// 定义学生结构体  
struct student {  
    int num;          // 学生序号  
    float score;      // 学生分数  
    struct student *next; // 指向下一个学生的指针  
};  
  
int n; // 全局变量,用于记录学生数量  
  
// 创建链表函数  
struct student *creat() {  
    struct student *head = NULL, *p1, *p2 = NULL; // 初始化头指针为NULL,p1用于新节点的分配,p2用于追踪前一个节点  
    p1 = (struct student *)malloc(LEN); // 为新节点分配内存  
    if (!p1) { // 检查内存分配是否成功  
        printf("Memory allocation failed!\n");  
        return NULL; // 如果内存分配失败,则返回NULL  
    }  
    printf("Input the num: ");  
    scanf("%d", &p1->num); // 输入学生序号  
    if (p1->num == 0) { // 如果学生序号为0,则释放该节点并返回NULL  
        free(p1);  
        return NULL;  
    }  
    printf("Input the score: ");  
    scanf("%f", &p1->score); // 输入学生分数  
    head = p1; // 将头指针指向第一个学生节点  
    n = 1; // 学生数量设为1  
    while (p1->num != 0) { // 当输入的学生序号不为0时,继续添加学生节点  
        p2 = p1; // p2追踪前一个节点  
        p1 = (struct student *)malloc(LEN); // 为新节点分配内存  
        if (!p1) { // 检查内存分配是否成功  
            printf("Memory allocation failed!\n");  
            break; // 如果内存分配失败,则跳出循环  
        }  
        printf("Input the num: ");  
        scanf("%d", &p1->num); // 输入学生序号  
        if (p1->num == 0) { // 如果学生序号为0,则释放该节点,设置前一个节点的next为NULL,并跳出循环  
            free(p1);  
            p2->next = NULL;  
            break;  
        }  
        printf("Input the score: ");  
        scanf("%f", &p1->score); // 输入学生分数  
        p2->next = p1; // 将前一个节点的next指向新节点  
        n++; // 学生数量加1  
    }  
    return head; // 返回头指针  
}  
  
// 删除节点函数  
struct student *del(struct student *head, int num) {  
    struct student *p1, *p2 = NULL; // p1用于遍历链表,p2用于追踪前一个节点  
    if (head == NULL) { // 检查链表是否为空  
        printf("This is an empty list.\n");  
        return NULL; // 如果链表为空,则返回NULL  
    }  
    p1 = head; // 将p1指向头节点  
    while (p1 != NULL && p1->num != num) { // 遍历链表,直到找到要删除的节点或遍历完整个链表  
        p2 = p1; // p2追踪前一个节点  
        p1 = p1->next; // p1指向下一个节点  
    }  
    if (p1 == NULL) { // 如果p1为NULL,说明没有找到要删除的节点  
        printf("%d not found.\n", num);  
    } else { // 如果找到了要删除的节点  
        if (p1 == head) { // 如果要删除的节点是头节点  
            head = p1->next; // 将头指针指向下一个节点  
        } else { // 如果要删除的节点不是头节点  
            p2->next = p1->next; // 将前一个节点的next指向要删除节点的下一个节点  
        }  
        printf("Deleted no: %d success!\n", num); // 打印删除成功的消息  
        free(p1); // 释放要删除的节点内存  
        n--; // 学生数量减1  
    }  
    return head; // 返回头指针  
}  
  
// 打印链表函数  
void print(struct student *head) {  
    struct student *p; // 定义遍历链表的指针  
    printf("There are %d records!\n\n", n); // 打印学生数量  
    p = head; // 将p指向头节点  
    while (p != NULL) { // 遍历链表,直到遍历完整个链表  
        printf("Student ID: %d, Score: %f\n", p->num, p->score); // 打印学生序号和分数  
        p = p->next; // p指向下一个节点  
    }  
}  
  
// 主函数  
int main() {  
    struct student *stu = NULL; // 初始化学生链表头指针为NULL  
    int numToDelete; // 定义要删除的学生序号变量  
  
    stu = creat(); // 调用创建链表函数,并将返回的头指针赋值给stu  
    print(stu);    // 调用打印链表函数,打印学生链表  
  
    printf("Input the num to delete: "); // 提示用户输入要删除的学生序号  
    scanf("%d", &numToDelete); // 读取用户输入的要删除的学生序号  
  
    stu = del(stu, numToDelete); // 调用删除节点函数,删除指定序号的学生节点,并将返回的头指针赋值给stu  
    print(stu);                  // 调用打印链表函数,打印删除节点后的学生链表  
  
    return 0; // 主函数返回0,表示程序正常结束  
}

最主要还是理解删除节点的思想和运行方式,多写几次就可以记住了

对链表的插入

#include <stdio.h>

#include<stdlib.h>
#define LEN sizeof(struct student)


//定义学生结构体
struct student
{
    int num;//学生学号
    float score;//学生成绩
    struct student *next;//指向下一个学生的指针

};

int n;//全局变量,用于记录学生数量(可选,也可不)

//创建链表函数
struct student *creat()
{
    struct student *head;//头指针
    struct student *p1,*p2;//p1用于新节点,p2用于遍历(开辟)
    n=0;//初始化学生数量为0
    p1=p2=(struct student *)malloc(LEN);//分配内存给新单元
    if(!p1)
    {
        printf("内存分配失败!");
        return NULL;
    }
    printf("intput the num and score of student (end with 0):\n");
    scanf("%d %f",&p1->num,&p1->score);//输入学生学号和成绩
    head=NULL;
    while(p1->num!=0)//当学号不为零时继续输入
    {
        n++;
        if(n==1)
        head=p1;//第一个学生作为头节点
        else
        p2->next=p1;//将新节点链接到链接末尾
        p2=p1;
        p1=(struct student *)malloc(LEN);//为下一个学生分配内存
        if(!p1)
        {
            printf("内存分配失败!");
            return head;
        }
        scanf("%d %f",&p1->num,&p1->score);//输入下一个学生的学号和成绩

    }
    p2->next=NULL;//链表末尾节点指向NULL
    return head;
};

//删除链表节点函数
struct student *del(struct student *head,int num)
{
    struct student *p1,*p2;
    if(head==NULL)
    {
        printf("链表是空的\n");
        return head;
    }
    p1=head;
    while(p1->num!=num&&p1->next!=NULL)//查找要删除的学生
    {
        p2=p1;
        p1=p1->next;

    }
    if(p1->num==num)//找到要删除的学生
    {
        if(p1==head)
         head=p1->next;//如果是头节点,则头指针指向下一个节点
        else
        p2->next=p1->next;//否则,将前一个节点的next指向要删除节点的下一个节点、
        printf("删除成功!\n");
        n--;//学生数量减一

    }
    else
    {
        printf("此学生没有被找到!\n");
    return head;
    }
};
//打印链表函数
void print(struct student *head)
{
    struct student *p;
    p=head;
    if(head!=NULL)
    {
        printf("链表为:\n");
        while(p!=NULL)//遍历链表并打印每个学生的学号和成绩
        {
            printf("%d %.2f\n",p->num,p->score);
            p=p->next;

        }
    }
    else
    {
        printf("链表为空链表!\n");
    }

}
//插入链表节点函数
struct student *insert(struct student *head,int position,int num,float score)
{
    struct student *p1,*p2=NULL;
    int i=0;

    p1=(struct student *)malloc(LEN);//为新节点分配内存
    if(!p1)
    {
        printf("内存分配失败!\n");
        return head;
    }
    p1->num=num;//设置新节点的学号和成绩
    p1->score=score;

    if(position==0||head==NULL)//插入到头节点之前或链表为空
    {
        p1->next=head;//新节点的next指向原头节点

        head=p1;//更新头节点为新节点
    }
    else
    {
        p2=head;//从头节点开始遍历
        while(p2!=NULL&&i<position-1)//遍历到指定位置的前一个节点
        {
            p2=p2->next;
            i++;
        }
        if(p2==NULL)//位置超出链表长度
        {
            free(p1);//释放新节点内存
            printf("插入位置超出界限!\n");

        }
        else//在p2之后插入新节点
        {
            p1->next=p2->next;//新节点next指向p2的next
            p2->next=p1;//p2的next指向新节点
        }
    }
    n++;//学生数量+1
    return head;//返回插入新节点后的链表的头指针

};

//主函数
int main()
{
    struct student *stu=NULL;//初始化学生链表头指针为NULL
    int numtodelete,numtoinsert,position;
    float scoretoinsert;

    stu=creat();//创建学生链表
    print(stu);//打印学生链表



    printf("输入要删除学生的学号:");

    scanf("%d",&numtodelete);

    printf("输入插入位置学号和成绩:");
    scanf("%d %d %f",&position,&numtoinsert,&scoretoinsert);

    stu=insert(stu,position,numtoinsert,scoretoinsert);//在指定位置插入新学生

    print(stu);//打印插入后的学生链表

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值