学期:2024-2025-1 学号:20241303 《计算机基础与程序设计》第十三周学习总结

作业信息

这个作业属于哪个课程 <班级的链接>(如2024-2025-1-计算机基础与程序设计
这个作业要求在哪里 <作业要求的链接>(如2024-2025-1计算机基础与程序设计第十三周作业)
这个作业的目标 <写上具体方面>加入云班课,参考本周学习资源;自学教材《C语言程序设计》第12章并完成云班课测试
作业正文 ... 本博客链接

教材学习内容总结

结构体

一、结构体的定义

  1. 基本定义形式
    • 结构体是一种用户自定义的数据类型,它允许将不同类型的数据组合在一起。定义结构体的基本语法如下:
    struct结构体名{
        数据类型 成员1;
        数据类型 成员2;
        //...
    };
    
    • 例如,定义一个表示学生信息的结构体:
    struct Student {
        char name[20];
        int age;
        float score;
    };
    
    • 这里定义了一个名为Student的结构体,它包含了一个字符数组name(用于存储学生姓名)、一个整型变量age(用于存储学生年龄)和一个单精度浮点型变量score(用于存储学生成绩)。
  2. 使用typedef定义结构体别名
    • 为了方便使用结构体类型,可以使用typedef关键字为结构体类型定义一个别名。语法如下:
    typedef struct结构体名{
        数据类型 成员1;
        数据类型 成员2;
        //...
    }结构体别名;
    
    • 例如,对于上面的Student结构体,可以这样定义别名:
    typedef struct Student {
        char name[20];
        int age;
        float score;
    }Stu;
    
    • 之后就可以使用Stu来声明结构体变量,如Stu stu1;,这样代码更加简洁。

二、结构体变量的声明和初始化

  1. 声明结构体变量
    • 在定义了结构体类型后,可以声明结构体变量。有以下两种常见方式:
    • 方式一:先定义结构体类型,再声明变量。
    struct Student {
        char name[20];
        int age;
        float score;
    };
    struct Student stu1;
    
    • 方式二:在定义结构体类型的同时声明变量。
    struct Student {
        char name[20];
        int age;
        float score;
    } stu1;
    
  2. 初始化结构体变量
    • 可以在声明结构体变量时对其进行初始化。
    • 例如,初始化一个Student结构体变量:
    struct Student stu1 = {"Tom", 18, 90.5};
    
    • 按照结构体成员定义的顺序,依次为每个成员赋初始值。如果只想初始化部分成员,可以这样做:
    struct Student stu2 = {.age = 19};
    
    • 这里只初始化了age成员,其他成员的值是未定义的(如果是全局变量,会被初始化为0;如果是局部变量,是随机值)。

三、结构体成员的访问

  1. 通过点运算符(.)访问成员
    • 对于普通的结构体变量,可以使用点运算符来访问结构体中的成员。
    • 例如,对于前面定义的stu1变量,如果要访问其姓名成员并输出,可以这样写:
    printf("Name: %s\n", stu1.name);
    
    • 如果要修改stu1的年龄成员,可以这样写:
    stu1.age = 20;
    
  2. 通过指针访问成员(->运算符)
    • 当有一个指向结构体的指针时,使用->运算符来访问结构体成员。
    • 例如,有如下代码:
    struct Student *pstu = &stu1;
    
    • 要访问stu1的成绩成员,可以这样写:
    printf("Score: %f\n", pstu->score);
    

四、结构体数组

  1. 定义和初始化结构体数组
    • 结构体数组是一个数组,其元素是结构体类型。定义结构体数组的语法如下:
    struct结构体名 数组名[数组大小];
    
    • 例如,定义一个包含3个Student结构体的数组:
    struct Student class[3];
    
    • 初始化结构体数组可以这样写:
    struct Student class[3] = {{"Alice", 17, 88.0}, {"Bob", 18, 92.0}, {"Cindy", 19, 85.0}};
    
  2. 访问结构体数组中的元素和成员
    • 可以通过数组下标来访问结构体数组中的元素,然后再使用点运算符或->运算符访问元素中的成员。
    • 例如,要输出第二个学生的姓名,可以这样写:
    printf("Second student's name: %s\n", class[1].name);
    

五、结构体的嵌套

  1. 定义嵌套结构体
    • 结构体中可以包含另一个结构体作为其成员。例如,定义一个包含日期结构体的学生结构体:
    struct Date {
        int year;
        int month;
        int day;
    };
    struct StudentWithDate {
        char name[20];
        struct Date birthday;
        int age;
    };
    
  2. 访问嵌套结构体中的成员
    • 访问嵌套结构体中的成员时,需要使用多个点运算符。例如,对于struct StudentWithDate类型的变量stu,要访问其生日的年份,可以这样写:
    struct StudentWithDate stu;
    printf("Birth year: %d\n", stu.birthday.year);
    

共用体

一、共用体的定义

  • 共用体(也称为联合体)是一种特殊的数据类型,它允许在同一段内存存储不同类型的数据,但在某一时刻,这段内存空间只能存储其中一种类型的数据。其定义语法如下:
union 共用体名 {
    数据类型 成员1;
    数据类型 成员2;
    //...
};
  • 例如,定义一个名为Data的共用体:
union Data {
    int i;
    float f;
    char str[20];
};
  • 这个共用体Data有三个成员,分别是整型i、单精度浮点型f和字符数组str。它们共享同一段内存空间。

二、共用体变量的声明和初始化

  1. 声明共用体变量
    • 与结构体类似,声明共用体变量有两种方式。
    • 方式一:先定义共用体类型,再声明变量。
    union Data {
        int i;
        float f;
        char str[20];
    };
    union Data data;
    
    • 方式二:在定义共用体类型的同时声明变量。
    union Data {
        int i;
        float f;
        char str[20];
    } data;
    
  2. 初始化共用体变量
    • 可以在声明共用体变量时对其进行初始化。不过要注意,由于共用体成员共享内存,初始化时只能初始化一个成员。
    • 例如:
    union Data data = {.i = 10};
    
    • 这里初始化了共用体Data的整型成员i为10。

三、共用体成员的访问

  • 访问共用体成员的方式和结构体类似,使用点运算符(.)。
  • 例如,对于上面声明的data变量,如果要访问其整型成员i并输出,可以这样写:
printf("The value of i: %d\n", data.i);
  • 如果要修改共用体成员的值,例如将i的值修改为20,可以这样写:
data.i = 20;

四、共用体的大小

  • 共用体的大小取决于其最大成员的大小。这是因为所有成员共享同一块内存空间,为了能够容纳最大的成员,共用体的大小至少要等于最大成员的大小。
  • 例如,在前面定义的union Data中,str成员是最大的(假设在这个环境中,一个int占4字节,一个float占4字节,str数组占20字节),所以union Data的大小通常是20字节。不过,在实际的编译器实现中,可能会因为字节对齐等因素而有所不同。字节对齐是为了提高内存访问效率,编译器可能会在成员之间插入一些填充字节。

五、共用体与结构体的区别

  1. 内存存储方式
    • 结构体的每个成员都有自己独立的内存空间,结构体变量的大小是所有成员大小之和(考虑字节对齐)。例如,一个结构体中有一个int(4字节)和一个float(4字节),它的大小至少是8字节。
    • 共用体的所有成员共享同一段内存空间,其大小取决于最大成员的大小。
  2. 数据使用特点
    • 结构体可以同时存储和访问多个不同类型的数据成员。例如,可以同时获取一个学生结构体中的姓名、年龄和成绩等多个信息。
    • 共用体在某一时刻只能存储和使用其中一个成员的数据。例如,对于前面的union Data,当存储了一个整型数据后,再存储一个浮点型数据,原来的整型数据就会被覆盖。

六、共用体的应用场景

  1. 节省内存空间
    • 在一些资源受限的嵌入式系统或对内存要求苛刻的应用中,当多个数据成员不会同时使用时,使用共用体可以节省内存。例如,在一个通信协议的数据包解析程序中,数据包可能有多种不同类型的负载(如整型数据、浮点型数据或者字符数组形式的数据),但每次只会处理其中一种类型的负载,这时可以使用共用体来表示数据包的负载部分。
  2. 实现数据类型的转换
    • 可以利用共用体来实现数据类型的转换。例如,将一个int类型的数据和一个float类型的数据进行位模式的转换。通过将它们放在一个共用体中,先给一个成员赋值,然后通过访问另一个成员来获取转换后的数据。不过这种方式要注意字节顺序等问题,因为不同的计算机体系结构可能有不同的字节顺序。

单项列表

  1. 定义
    • 在C语言中,单项列表(也称为单链表)是一种常见的数据结构。它由一系列节点组成,每个节点包含数据部分和指向下一个节点的指针。节点的结构通常可以定义如下:
    typedef struct Node {
        int data;    // 数据部分,可以是任何类型,这里以int为例
        struct Node* next;  // 指向下一个节点的指针
    } Node;
    
    • 这里使用typedefstruct Node结构体定义了别名Node,方便后续使用。每个节点的数据部分存储实际的数据(这里是int类型的数据),next指针用于连接下一个节点,从而形成链表结构。
  2. 创建节点
    • 要创建一个节点,可以使用malloc函数动态分配内存。例如:
    Node* createNode(int value) {
        Node* newNode = (Node*)malloc(sizeof(Node));
        if (newNode == NULL) {
            // 内存分配失败
            return NULL;
        }
        newNode->data = value;
        newNode->next = NULL;
        return newNode;
    }
    
    • 这个函数接受一个int值作为参数,用于初始化节点的数据部分。首先通过malloc分配足够的内存来存储Node结构体,如果分配成功,将传入的值赋给节点的数据部分,并且将next指针初始化为NULL,表示这个节点目前是链表的最后一个节点,最后返回新创建的节点指针。
  3. 构建单链表
    • 可以通过逐个添加节点来构建单链表。例如,下面的函数可以将一个新节点添加到链表的头部:
    Node* insertAtHead(Node* head, int value) {
        Node* newNode = createNode(value);
        if (newNode == NULL) {
            return head;
        }
        newNode->next = head;
        return newNode;
    }
    
    • 这个函数首先调用createNode函数创建一个新节点。如果创建成功,将新节点的next指针指向当前的头节点head,然后将新节点作为新的头节点返回。这样就实现了在链表头部插入节点的操作。
  4. 遍历单链表
    • 遍历单链表可以通过从头节点开始,沿着next指针逐个访问节点,直到遇到NULL指针为止。例如:
    void traverseList(Node* head) {
        Node* current = head;
        while (current!= NULL) {
            printf("%d ", current->data);
            current = current->next;
        }
        printf("\n");
    }
    
    • 这个函数接受一个头节点指针head,首先将一个临时指针current指向头节点。然后在while循环中,只要current指针不为NULL,就输出当前节点的数据部分,并将current指针移动到下一个节点。当current指针为NULL时,说明已经遍历完整个链表,最后输出一个换行符。
  5. 查找节点
    • 可以在单链表中查找特定数据的节点。例如,下面的函数用于在链表中查找数据为target的节点:
    Node* searchNode(Node* head, int target) {
        Node* current = head;
        while (current!= NULL) {
            if (current->data == target) {
                return current;
            }
            current = current->next;
        }
        return NULL;
    }
    
    • 这个函数和遍历链表的过程类似,在遍历过程中,当遇到节点的数据部分等于target时,就返回该节点的指针。如果遍历完整个链表都没有找到符合条件的节点,就返回NULL
  6. 删除节点
    • 删除单链表中的节点有多种情况。例如,删除头节点可以这样做:
    Node* deleteAtHead(Node* head) {
        if (head == NULL) {
            return NULL;
        }
        Node* temp = head;
        head = head->next;
        free(temp);
        return head;
    }
    
    • 这个函数首先判断链表是否为空,如果为空则直接返回NULL。如果不为空,将头节点保存在一个临时指针temp中,然后将头节点更新为原来头节点的下一个节点,最后释放原来头节点所占用的内存并返回新的头节点。

    • 删除链表中除头节点外的其他节点稍微复杂一些。例如,要删除数据为value的节点(假设链表中存在该节点且不是头节点):

    Node* deleteNode(Node* head, int value) {
        if (head == NULL) {
            return NULL;
        }
        Node* current = head;
        Node* prev = NULL;
        while (current!= NULL && current->data!= value) {
            prev = current;
            current = current->next;
        }
        if (current == NULL) {
            // 未找到要删除的节点
            return head;
        }
        if (prev == NULL) {
            // 要删除的节点是头节点
            head = head->next;
        } else {
            prev->next = current->next;
        }
        free(current);
        return head;
    }
    
    • 这个函数使用两个指针currentprevcurrent用于遍历链表查找要删除的节点,prev用于记录current的前一个节点。在找到要删除的节点后,如果prevNULL,说明要删除的是头节点,直接更新头节点;否则,将prevnext指针指向current的下一个节点,从而将current节点从链表中删除,最后释放current节点占用的内存并返回更新后的头节点。

基于AI的学习











代码调试中的问题和解决过程

问题1:
输出倒三角形图案
Description

输入正整数n,输出n行由*构成的倒置等腰三角形填充图案。

Input

输入正整数n,其中n大于1且不超过30,输入格式为:"%d"。

Output

输出n行由*构成的倒置等腰三角形填充图案。
方法:

#include<stdio.h>
int main()
{
    int n,i,j;
    char a='*';
    char b=' ';
    scanf("%d",&n);
    for(i=n; i>0; i--)
    {
        for(j=i+1;j<=n;j++)
        {
            printf("%c",b);
        }
        for(j=2*i-1; j>0; j--)
        {
            printf("%c",a);
        }
        printf("\n");
    }
    return 0;
}

其他(感悟、思考等,可选)

复习

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第十二周 2100/3000 1/2 20/20
第十三周 2300/3000 1/4 18/38
第十四周 500/1000 3/7 22/60
第十五周 300/1300 2/9 30/90
posted @ 2024-12-21 16:28  段蕾  阅读(0)  评论(0编辑  收藏  举报