news 2026/4/18 15:26:39

实验七记录,加深对结构和联合的理解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
实验七记录,加深对结构和联合的理解

完成比完美更重要

实验7 结构与联合实验

7.1实验目的

1.通过实验,熟悉和掌握结构的说明和引用、结构的指针、结构数组、以及函数中使用结构的方法。

2.通过实验,掌握动态储存分配函数的用法,掌握自引用结构,单向链表的创建、遍历、结点的增删、查找等操作。

3.了解字段结构和联合的用法。

7.2实验题目及要求

1.表达式求值的程序验证题

设有说明:

char u[]="UVWXYZ";

char v[]="xyz";

struct T{

int x;

char c;

char *t;

}a[]={{11,ˊAˊ,u},{100, ˊBˊ,v}},*p=a;

请先自己计算下面表达式的值,然后通过编程计算来加以验证。(各表达式相互无关)

序号

表达式

计算值

验证值

1

(++p)->x

2

p++,p->c

3

*p++->t,*p->t

4

*(++p)->t

5

*++p->t

6

++*p->t

解答:

  1. 计算值从上至下依次为:

100

B

U,x

x

V

V

  1. 用以验证计算值的源程序清单:
  2. #include<stdio.h>
  3. #include<string.h>
  4. structT{
  5. intx;
  6. charc;
  7. char* t;
  8. };
  9. charu_data[] ="UVWXYZ";
  10. charv_data[] ="xyz";
  11. constcharu_backup[] ="UVWXYZ";
  12. constcharv_backup[] ="xyz";
  13. structTa_data[] = {
  14. {11,'A', u_data},
  15. {100,'B', v_data}
  16. };
  17. structT* p = a_data;
  18. voidreset_p() {
  19. p = a_data;
  20. memcpy(u_data, u_backup,sizeof(u_backup));
  21. memcpy(v_data, v_backup,sizeof(v_backup));
  22. a_data[0].t = u_data;
  23. a_data[1].t = v_data;
  24. }
  25. intmain() {
  26. intval_int;
  27. charval_char;
  28. charval_char1, val_char2;
  29. printf("验证值:\n");
  30. // 1. (++p)->x
  31. reset_p();
  32. val_int = (++p)->x;
  33. printf("1: %d\n", val_int);
  34. // 2. p++, p->c
  35. reset_p();
  36. val_char = (p++, p->c);
  37. printf("2: %c\n", val_char);
  38. // 3. *p++->t, *p->t
  39. reset_p();
  40. val_char1 = *p++->t;
  41. val_char2 = *p->t;
  42. printf("3: %c, %c\n", val_char1, val_char2);
  43. // 4. *(++p)->t
  44. reset_p();
  45. val_char = *(++p)->t;
  46. printf("4: %c\n", val_char);
  47. // 5. *++p->t
  48. reset_p();
  49. val_char = *++p->t;
  50. printf("5: %c\n", val_char);
  51. // 6. ++*p->t
  52. reset_p();
  53. val_char = ++ * p->t;
  54. printf("6: %c\n", val_char);
  55. return0;
  56. }

(3)验证程序的运行截图:

图7-1程序验证题对应程序的运行截图

2.源程序修改替换题

给定一批整数,以0作为结束标志且不作为结点,将其建成一个先进先出的链表,先进先出链表的指头指针始终指向最先创建的结点(链头),先建结点指向后建结点,后建结点始终是尾结点。

  1. 源程序中存在什么样的错误(先观察执行结果)?对程序进行修改、调试,使之能够正确完成指定任务。

源程序如下:

#include "stdio.h"

#include "stdlib.h"

struct s_list{

int data; /* 数据域 */

struct s_list *next; /* 指针域 */

} ;

void create_list (struct s_list *headp,int *p);

void main(void)

{

struct s_list *head=NULL,*p;

int s[]={1,2,3,4,5,6,7,8,0}; /* 0为结束标记 */

create_list(head,s); /* 创建新链表 */

p=head; /*遍历指针p指向链头 */

while(p){

printf("%d\t",p->data); /* 输出数据域的值 */

p=p->next; /*遍历指针p指向下一结点 */

}

printf("\n");

}

void create_list(struct s_list *headp,int *p)

{

struct s_list * loc_head=NULL,*tail;

if(p[0]==0) /* 相当于*p==0 */

;

else { /* loc_head指向动态分配的第一个结点 */

loc_head=(struct s_list *)malloc(sizeof(struct s_list));

loc_head->data=*p++; /* 对数据域赋值 */

tail=loc_head; /* tail指向第一个结点 */

while(*p){ /* tail所指结点的指针域指向动态创建的结点 */

tail->next=(struct s_list *)malloc(sizeof(struct s_list));

tail=tail->next; /* tail指向新创建的结点 */

tail->data=*p++; /* 向新创建的结点的数据域赋值 */

}

tail->next=NULL; /* 对指针域赋NULL值 */

}

headp=loc_head; /* 使头指针headp指向新创建的链表 */

}

解答:

  1. 错误修改:
        1. main中对notes的调用错误
        2. notes的声明和定义错误,应使用二级指针
  2. 修改后的源程序清单:

#include <stdio.h>

#include <stdlib.h>

struct s_list {

int data;

struct s_list *next;

};

// 接受二级指针:struct s_list **

void create_list(struct s_list **headpp, int *p);

void main(void)

{

struct s_list *head = NULL, *p;

int s[] = {1, 2, 3, 4, 5, 6, 7, 8, 0};

// 传递 head 的地址

create_list(&head, s);

p = head;

printf("--- FIFO 链表输出 ---\n");

while(p) {

printf("%d\t", p->data);

p = p->next;

}

printf("\n");

}

// 修改后的 create_list 函数(FIFO)

void create_list(struct s_list **headpp, int *p)

{

struct s_list *loc_head = NULL, *tail;

if (*p == 0)

; // 数组为空,loc_head 保持 NULL

else {

// 1. 创建第一个结点

loc_head = (struct s_list *)malloc(sizeof(struct s_list));

if (loc_head == NULL) return; // 检查内存分配

loc_head->data = *p++;

tail = loc_head;

// 2. 依次创建后续结点 (尾部插入)

while (*p) {

tail->next = (struct s_list *)malloc(sizeof(struct s_list));

if (tail->next == NULL) break; // 检查内存分配

tail = tail->next;

tail->data = *p++;

}

tail->next = NULL;

}

// 3. 关键修正:通过二级指针修改 main 函数中 head 的值

*headpp = loc_head;

}

  1. 对应程序的运行截图:

图7-2程序修改后的运行截图

  1. 修改替换create_list函数,将其建成一个后进先出的链表,后进先出链表的头指针始终指向最后创建的结点(链头),后建结点指向先建结点,先建结点始终是尾结点。

解答:

  1. 替换后的源程序清单:

// 修改后的 create_list 函数(LIFO - 头部插入)

void create_list_lifo(struct s_list **headpp, int *p)

{

struct s_list *new_node = NULL; // 用于指向每次新创建的结点

// 初始化 headpp 指向的头指针为 NULL

*headpp = NULL;

while (*p != 0) {

// 1. 创建新结点 (new_node)

new_node = (struct s_list *)malloc(sizeof(struct s_list));

if (new_node == NULL) {

printf("内存分配失败!\n");

return;

}

// 2. 填充数据域

new_node->data = *p;

p++; // 移动到下一个整数

// 3. 核心 LIFO 逻辑:头部插入

// 新结点的 next 指向当前的链头 (*headpp)

new_node->next = *headpp;

// 4. 更新链头:将新结点设置为新的链头

*headpp = new_node;

}

// 循环结束后,*headpp 就是指向最后创建的结点 (链头)

}

  1. 对应程序的运行截图:

图7-3程序替换题的运行截图

3程序设计

以下(1)至(3)题对应Educoder 教学平台“C语言实验”课程,实验7,第17关实验7-1、第18关实验7-2,以及第19关实验7-3。

(1)本关任务:用单向链表建立一张班级成绩单,包括每个学生的学号、姓名、英语、高等数学、普通物理、C语言程序设计四门课程的成绩。用菜单实现下列功能:

① 输入每个学生的各项信息。

② 输出每个学生的各项信息。

③ 修改指定学生的指定数据项的内容。

④ 统计每个同学的平均成绩(保留2位小数)。

⑤ 输出各位同学的学号、姓名、四门课程的总成绩和平均成绩。

解答:

  1. 解题思路:

数据结构设计

定义 struct student 结构体,包含学号、姓名、四门课程成绩及平均成绩字段,并用 next 指针构成链表。

建立链表

动态申请内存(malloc),按输入顺序将新节点尾插到链表中,构成学生信息链表。

遍历与输出

从头指针开始遍历链表即可输出所有学生信息。

修改功能

根据学号查找节点,选择要修改的成绩字段并更新其值。

计算平均成绩与输出

遍历链表计算每个学生的平均分并存入节点,再次遍历输出总分与平均分。

  1. 源程序清单:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

typedef struct student {

char id[20];

char name[20];

float eng, math, phy, cprog;

float avg;

struct student *next;

} Stu;

Stu* create(); //① 输入

void print(Stu *head); //② 输出

void modify(Stu *head);//③ 修改

void calcAvg(Stu *head);//④ 统计平均

void printAvg(Stu *head);//⑤ 输出总分和平均分

int menu();

int main() {

Stu *head = NULL;

int choice;

while(1) {

choice = menu();

switch(choice) {

case 1: head = create(); break;

case 2: print(head); break;

case 3: modify(head); break;

case 4: calcAvg(head); break;

case 5: printAvg(head); break;

case 0: exit(0);

default: printf("输入错误!\n");

}

}

return 0;

}

int menu() {

printf("\n===== 学生成绩管理系统 =====\n");

printf("1. 输入学生信息\n");

printf("2. 输出学生信息\n");

printf("3. 修改指定学生信息\n");

printf("4. 计算每位学生平均成绩\n");

printf("5. 输出总分与平均分\n");

printf("0. 退出\n");

printf("请选择:");

int c; scanf("%d", &c);

return c;

}

Stu* create() {

Stu *head = NULL, *p, *tail = NULL;

int n;

printf("请输入学生人数: ");

scanf("%d", &n);

for(int i = 0; i < n; i++) {

p = (Stu*)malloc(sizeof(Stu));

printf("输入 学号 姓名 英语 高数 物理 C语言 成绩:\n");

scanf("%s %s %f %f %f %f", p->id, p->name, &p->eng, &p->math, &p->phy, &p->cprog);

p->avg = 0;

p->next = NULL;

if(head == NULL) head = p;

else tail->next = p;

tail = p;

}

return head;

}

void print(Stu *head) {

Stu *p = head;

printf("\n学号\t姓名\t英语\t高数\t物理\tC语言\n");

while(p) {

printf("%s\t%s\t%.2f\t%.2f\t%.2f\t%.2f\n",

p->id, p->name, p->eng, p->math, p->phy, p->cprog);

p = p->next;

}

}

void modify(Stu *head) {

char id[20];

printf("输入要修改的学生学号: ");

scanf("%s", id);

Stu *p = head;

while(p && strcmp(p->id, id) != 0) p = p->next;

if(!p) { printf("未找到该学生!\n"); return; }

printf("选择要修改的科目:1英语 2高数 3物理 4C语言\n");

int c; scanf("%d", &c);

float score;

printf("输入新成绩: "); scanf("%f", &score);

if(c == 1) p->eng = score;

else if(c == 2) p->math = score;

else if(c == 3) p->phy = score;

else if(c == 4) p->cprog = score;

else printf("输入无效\n");

}

void calcAvg(Stu *head) {

Stu *p = head;

while(p) {

p->avg = (p->eng + p->math + p->phy + p->cprog) / 4.0;

p = p->next;

}

printf("平均分计算完成!\n");

}

void printAvg(Stu *head) {

Stu *p = head;

printf("\n学号\t姓名\t总分\t平均分\n");

while(p) {

float sum = p->eng + p->math + p->phy + p->cprog;

printf("%s\t%s\t%.2f\t%.2f\n", p->id, p->name, sum, p->avg);

p = p->next;

}

}

  1. 测试:
  1. 各项功能的运行截图:

图7-4程序设计题(1)的运行截图

(2)本关任务:对程序设计题第(1)题的程序,⑥增加按照平均成绩进行升序排序的函数,写出用交换结点数据域的方法升序排序的函数,排序可用选择法或冒泡法。

解答:

  1. 解题思路:

前提条件

平均成绩已计算完成(任务一的基础)。

排序策略

可使用冒泡法或选择法本质都是比较两个节点的 avg 值。

交换方式

不改变节点之间指针关系,而是直接交换结构体中的数据项(包括姓名、分数等字段)。

  1. 源程序清单:“

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

typedef struct student {

char id[20];

char name[20];

float eng, math, phy, cprog;

float avg;

struct student *next;

} Stu;

Stu* create(); //① 输入

void print(Stu *head); //② 输出

void modify(Stu *head);//③ 修改

void calcAvg(Stu *head);//④ 统计平均

void printAvg(Stu *head);//⑤ 输出总分和平均分

void sortDataSwap(Stu *head);

int menu();

int main() {

Stu *head = NULL;

int choice;

while(1) {

choice = menu();

switch(choice) {

case 1: head = create(); break;

case 2: print(head); break;

case 3: modify(head); break;

case 4: calcAvg(head); break;

case 5: printAvg(head); break;

case 6: sortDataSwap(head); break;

case 0: exit(0);

default: printf("输入错误!\n");

}

}

return 0;

}

int menu() {

printf("\n===== 学生成绩管理系统 =====\n");

printf("1. 输入学生信息\n");

printf("2. 输出学生信息\n");

printf("3. 修改指定学生信息\n");

printf("4. 计算每位学生平均成绩\n");

printf("5. 输出总分与平均分\n");

printf("6. 按平均成绩排序(交换数据域)\n");

printf("0. 退出\n");

printf("请选择:");

int c; scanf("%d", &c);

return c;

}

Stu* create() {

Stu *head = NULL, *p, *tail = NULL;

int n;

printf("请输入学生人数: ");

scanf("%d", &n);

for(int i = 0; i < n; i++) {

p = (Stu*)malloc(sizeof(Stu));

printf("输入 学号 姓名 英语 高数 物理 C语言 成绩:\n");

scanf("%s %s %f %f %f %f", p->id, p->name, &p->eng, &p->math, &p->phy, &p->cprog);

p->avg = 0;

p->next = NULL;

if(head == NULL) head = p;

else tail->next = p;

tail = p;

}

return head;

}

void print(Stu *head) {

Stu *p = head;

printf("\n学号\t姓名\t英语\t高数\t物理\tC语言\n");

while(p) {

printf("%s\t%s\t%.2f\t%.2f\t%.2f\t%.2f\n",

p->id, p->name, p->eng, p->math, p->phy, p->cprog);

p = p->next;

}

}

void modify(Stu *head) {

char id[20];

printf("输入要修改的学生学号: ");

scanf("%s", id);

Stu *p = head;

while(p && strcmp(p->id, id) != 0) p = p->next;

if(!p) { printf("未找到该学生!\n"); return; }

printf("选择要修改的科目:1英语 2高数 3物理 4C语言\n");

int c; scanf("%d", &c);

float score;

printf("输入新成绩: "); scanf("%f", &score);

if(c == 1) p->eng = score;

else if(c == 2) p->math = score;

else if(c == 3) p->phy = score;

else if(c == 4) p->cprog = score;

else printf("输入无效\n");

}

void calcAvg(Stu *head) {

Stu *p = head;

while(p) {

p->avg = (p->eng + p->math + p->phy + p->cprog) / 4.0;

p = p->next;

}

printf("平均分计算完成!\n");

}

void printAvg(Stu *head) {

Stu *p = head;

printf("\n学号\t姓名\t总分\t平均分\n");

while(p) {

float sum = p->eng + p->math + p->phy + p->cprog;

printf("%s\t%s\t%.2f\t%.2f\n", p->id, p->name, sum, p->avg);

p = p->next;

}

}

void sortDataSwap(Stu *head) {

Stu *p, *q;

Stu temp;

for(p = head; p && p->next; p = p->next) {

for(q = p->next; q; q = q->next) {

if(p->avg > q->avg) { // 升序

temp = *p; *p = *q; *q = temp;

// 修正 next 指针被交换的问题

Stu *t = p->next;

p->next = q->next;

q->next = t;

}

}

}

printf("已按平均成绩升序排序(数据域交换)!\n");

}

  1. 测试:
  1. 各项功能的运行截图:

图7-5程序设计题(2)的运行截图

  1. 本关任务:对程序设计题第(2)题,进一步写出用交换结点指针域的方法升序排序的函数。
  1. 解题思路:

指针操作代替数据交换

找出当前循环中平均分最小的节点,用指针操作将其移动到正确位置。

排序方法

通常采用选择排序思想:每轮从剩余链表中找出最小 avg 节点,并将指针调整到指定位置

  1. 源程序清单:

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

typedefstructstudent{

charid[20];

charname[20];

floateng, math, phy, cprog;

floatavg;

structstudent* next;

}Stu;

Stu* create();//输入

voidprint(Stu*head);//输出

voidmodify(Stu*head);//修改

voidcalcAvg(Stu*head);//统计平均

voidprintAvg(Stu*head);//输出总分和平均分

voidsortDataSwap(Stu*head);

Stu* sortPointerSwap(Stu*head);

intmenu();

intmain() {

Stu* head =NULL;

intchoice;

while(1) {

choice = menu();

switch(choice) {

case1: head = create();break;

case2: print(head);break;

case3: modify(head);break;

case4: calcAvg(head);break;

case5: printAvg(head);break;

case6: sortDataSwap(head);break;

case7: head = sortPointerSwap(head);break;

case0: exit(0);

default: printf("输入错误!\n");

}

}

return0;

}

intmenu() {

printf("\n=====学生成绩管理系统=====\n");

printf("1.输入学生信息\n");

printf("2.输出学生信息\n");

printf("3.修改指定学生信息\n");

printf("4.计算每位学生平均成绩\n");

printf("5.输出总分与平均分\n");

printf("6.按平均成绩排序(交换数据域)\n");

printf("7.按平均成绩排序(交换指针域)\n");

printf("0.退出\n");

printf("请选择:");

intc; scanf("%d", &c);

returnc;

}

Stu* create() {

Stu* head =NULL, * p, * tail =NULL;

intn;

printf("请输入学生人数: ");

scanf("%d", &n);

for(inti = 0; i < n; i++) {

p = (Stu*)malloc(sizeof(Stu));

printf("输入学号姓名英语高数物理C语言成绩:\n");

scanf("%s %s %f %f %f %f", p->id, p->name, &p->eng, &p->math, &p->phy, &p->cprog);

p->avg = 0;

p->next =NULL;

if(head ==NULL) head = p;

elsetail->next = p;

tail = p;

}

returnhead;

}

voidprint(Stu*head) {

Stu* p =head;

printf("\n学号\t姓名\t英语\t高数\t物理\tC语言\n");

while(p) {

printf("%s\t%s\t%.2f\t%.2f\t%.2f\t%.2f\n",

p->id, p->name, p->eng, p->math, p->phy, p->cprog);

p = p->next;

}

}

voidmodify(Stu*head) {

charid[20];

printf("输入要修改的学生学号: ");

scanf("%s", id);

Stu* p =head;

while(p && strcmp(p->id, id) != 0) p = p->next;

if(!p) { printf("未找到该学生!\n");return; }

printf("选择要修改的科目:1英语2高数3物理4C语言\n");

intc; scanf("%d", &c);

floatscore;

printf("输入新成绩: "); scanf("%f", &score);

if(c == 1) p->eng = score;

elseif(c == 2) p->math = score;

elseif(c == 3) p->phy = score;

elseif(c == 4) p->cprog = score;

elseprintf("输入无效\n");

}

voidcalcAvg(Stu*head) {

Stu* p =head;

while(p) {

p->avg = (p->eng + p->math + p->phy + p->cprog) / 4.0;

p = p->next;

}

printf("平均分计算完成!\n");

}

voidprintAvg(Stu*head) {

Stu* p =head;

printf("\n学号\t姓名\t总分\t平均分\n");

while(p) {

floatsum = p->eng + p->math + p->phy + p->cprog;

printf("%s\t%s\t%.2f\t%.2f\n", p->id, p->name, sum, p->avg);

p = p->next;

}

}

voidsortDataSwap(Stu*head) {

Stu* p, * q;

Stutemp;

for(p =head; p && p->next; p = p->next) {

for(q = p->next; q; q = q->next) {

if(p->avg > q->avg) {//升序

temp = *p; *p = *q; *q = temp;

//修正next指针被交换的问题

Stu* t = p->next;

p->next = q->next;

q->next = t;

}

}

}

printf("已按平均成绩升序排序(数据域交换)!\n");

}

Stu* sortPointerSwap(Stu*head) {

if(!head)returnhead;

Studummy, * p, * q, * minPrev, * prev;

dummy.next =head;

prev = &dummy;

while(prev->next) {

minPrev = prev;

q = prev->next;

while(q->next) {

if(q->next->avg < minPrev->next->avg)

minPrev = q;

q = q->next;

}

if(minPrev != prev) {//交换节点指针

Stu* minNode = minPrev->next;

minPrev->next = minNode->next;

minNode->next = prev->next;

prev->next = minNode;

}

prev = prev->next;

}

printf("已按平均成绩升序排序(指针域交换)!\n");

returndummy.next;

}

  1. 测试:
  1. 各项功能的运行截图:

图7-6程序设计题(3)的运行截图

7.3实验小结

通过本次链表成绩管理系统的实现,我不仅掌握了单向链表的建立、遍历和修改等基本操作,还深入理解了结构体在数据组织中的作用;在完成平均成绩计算和排序功能的过程中,我体会到不同排序方法在链表结构中的实现差异,尤其是交换数据域与交换指针域的逻辑思想,使我对链表在动态数据管理中的优势有了更加直观而深刻的认识,也提升了我分析问题和模块化设计程序的能力。

实验7 结构与联合实验

7.1实验目的

1.通过实验,熟悉和掌握结构的说明和引用、结构的指针、结构数组、以及函数中使用结构的方法。

2.通过实验,掌握动态储存分配函数的用法,掌握自引用结构,单向链表的创建、遍历、结点的增删、查找等操作。

3.了解字段结构和联合的用法。

7.2实验题目及要求

1.表达式求值的程序验证题

设有说明:

char u[]="UVWXYZ";

char v[]="xyz";

struct T{

int x;

char c;

char *t;

}a[]={{11,ˊAˊ,u},{100, ˊBˊ,v}},*p=a;

请先自己计算下面表达式的值,然后通过编程计算来加以验证。(各表达式相互无关)

序号

表达式

计算值

验证值

1

(++p)->x

2

p++,p->c

3

*p++->t,*p->t

4

*(++p)->t

5

*++p->t

6

++*p->t

解答:

  1. 计算值从上至下依次为:

100

B

U,x

x

V

V

  1. 用以验证计算值的源程序清单:
  2. #include<stdio.h>
  3. #include<string.h>
  4. structT{
  5. intx;
  6. charc;
  7. char* t;
  8. };
  9. charu_data[] ="UVWXYZ";
  10. charv_data[] ="xyz";
  11. constcharu_backup[] ="UVWXYZ";
  12. constcharv_backup[] ="xyz";
  13. structTa_data[] = {
  14. {11,'A', u_data},
  15. {100,'B', v_data}
  16. };
  17. structT* p = a_data;
  18. voidreset_p() {
  19. p = a_data;
  20. memcpy(u_data, u_backup,sizeof(u_backup));
  21. memcpy(v_data, v_backup,sizeof(v_backup));
  22. a_data[0].t = u_data;
  23. a_data[1].t = v_data;
  24. }
  25. intmain() {
  26. intval_int;
  27. charval_char;
  28. charval_char1, val_char2;
  29. printf("验证值:\n");
  30. // 1. (++p)->x
  31. reset_p();
  32. val_int = (++p)->x;
  33. printf("1: %d\n", val_int);
  34. // 2. p++, p->c
  35. reset_p();
  36. val_char = (p++, p->c);
  37. printf("2: %c\n", val_char);
  38. // 3. *p++->t, *p->t
  39. reset_p();
  40. val_char1 = *p++->t;
  41. val_char2 = *p->t;
  42. printf("3: %c, %c\n", val_char1, val_char2);
  43. // 4. *(++p)->t
  44. reset_p();
  45. val_char = *(++p)->t;
  46. printf("4: %c\n", val_char);
  47. // 5. *++p->t
  48. reset_p();
  49. val_char = *++p->t;
  50. printf("5: %c\n", val_char);
  51. // 6. ++*p->t
  52. reset_p();
  53. val_char = ++ * p->t;
  54. printf("6: %c\n", val_char);
  55. return0;
  56. }

(3)验证程序的运行截图:

图7-1程序验证题对应程序的运行截图

2.源程序修改替换题

给定一批整数,以0作为结束标志且不作为结点,将其建成一个先进先出的链表,先进先出链表的指头指针始终指向最先创建的结点(链头),先建结点指向后建结点,后建结点始终是尾结点。

  1. 源程序中存在什么样的错误(先观察执行结果)?对程序进行修改、调试,使之能够正确完成指定任务。

源程序如下:

#include "stdio.h"

#include "stdlib.h"

struct s_list{

int data; /* 数据域 */

struct s_list *next; /* 指针域 */

} ;

void create_list (struct s_list *headp,int *p);

void main(void)

{

struct s_list *head=NULL,*p;

int s[]={1,2,3,4,5,6,7,8,0}; /* 0为结束标记 */

create_list(head,s); /* 创建新链表 */

p=head; /*遍历指针p指向链头 */

while(p){

printf("%d\t",p->data); /* 输出数据域的值 */

p=p->next; /*遍历指针p指向下一结点 */

}

printf("\n");

}

void create_list(struct s_list *headp,int *p)

{

struct s_list * loc_head=NULL,*tail;

if(p[0]==0) /* 相当于*p==0 */

;

else { /* loc_head指向动态分配的第一个结点 */

loc_head=(struct s_list *)malloc(sizeof(struct s_list));

loc_head->data=*p++; /* 对数据域赋值 */

tail=loc_head; /* tail指向第一个结点 */

while(*p){ /* tail所指结点的指针域指向动态创建的结点 */

tail->next=(struct s_list *)malloc(sizeof(struct s_list));

tail=tail->next; /* tail指向新创建的结点 */

tail->data=*p++; /* 向新创建的结点的数据域赋值 */

}

tail->next=NULL; /* 对指针域赋NULL值 */

}

headp=loc_head; /* 使头指针headp指向新创建的链表 */

}

解答:

  1. 错误修改:
        1. main中对notes的调用错误
        2. notes的声明和定义错误,应使用二级指针
  2. 修改后的源程序清单:

#include <stdio.h>

#include <stdlib.h>

struct s_list {

int data;

struct s_list *next;

};

// 接受二级指针:struct s_list **

void create_list(struct s_list **headpp, int *p);

void main(void)

{

struct s_list *head = NULL, *p;

int s[] = {1, 2, 3, 4, 5, 6, 7, 8, 0};

// 传递 head 的地址

create_list(&head, s);

p = head;

printf("--- FIFO 链表输出 ---\n");

while(p) {

printf("%d\t", p->data);

p = p->next;

}

printf("\n");

}

// 修改后的 create_list 函数(FIFO)

void create_list(struct s_list **headpp, int *p)

{

struct s_list *loc_head = NULL, *tail;

if (*p == 0)

; // 数组为空,loc_head 保持 NULL

else {

// 1. 创建第一个结点

loc_head = (struct s_list *)malloc(sizeof(struct s_list));

if (loc_head == NULL) return; // 检查内存分配

loc_head->data = *p++;

tail = loc_head;

// 2. 依次创建后续结点 (尾部插入)

while (*p) {

tail->next = (struct s_list *)malloc(sizeof(struct s_list));

if (tail->next == NULL) break; // 检查内存分配

tail = tail->next;

tail->data = *p++;

}

tail->next = NULL;

}

// 3. 关键修正:通过二级指针修改 main 函数中 head 的值

*headpp = loc_head;

}

  1. 对应程序的运行截图:

图7-2程序修改后的运行截图

  1. 修改替换create_list函数,将其建成一个后进先出的链表,后进先出链表的头指针始终指向最后创建的结点(链头),后建结点指向先建结点,先建结点始终是尾结点。

解答:

  1. 替换后的源程序清单:

// 修改后的 create_list 函数(LIFO - 头部插入)

void create_list_lifo(struct s_list **headpp, int *p)

{

struct s_list *new_node = NULL; // 用于指向每次新创建的结点

// 初始化 headpp 指向的头指针为 NULL

*headpp = NULL;

while (*p != 0) {

// 1. 创建新结点 (new_node)

new_node = (struct s_list *)malloc(sizeof(struct s_list));

if (new_node == NULL) {

printf("内存分配失败!\n");

return;

}

// 2. 填充数据域

new_node->data = *p;

p++; // 移动到下一个整数

// 3. 核心 LIFO 逻辑:头部插入

// 新结点的 next 指向当前的链头 (*headpp)

new_node->next = *headpp;

// 4. 更新链头:将新结点设置为新的链头

*headpp = new_node;

}

// 循环结束后,*headpp 就是指向最后创建的结点 (链头)

}

  1. 对应程序的运行截图:

图7-3程序替换题的运行截图

3程序设计

以下(1)至(3)题对应Educoder 教学平台“C语言实验”课程,实验7,第17关实验7-1、第18关实验7-2,以及第19关实验7-3。

(1)本关任务:用单向链表建立一张班级成绩单,包括每个学生的学号、姓名、英语、高等数学、普通物理、C语言程序设计四门课程的成绩。用菜单实现下列功能:

① 输入每个学生的各项信息。

② 输出每个学生的各项信息。

③ 修改指定学生的指定数据项的内容。

④ 统计每个同学的平均成绩(保留2位小数)。

⑤ 输出各位同学的学号、姓名、四门课程的总成绩和平均成绩。

解答:

  1. 解题思路:

数据结构设计

定义 struct student 结构体,包含学号、姓名、四门课程成绩及平均成绩字段,并用 next 指针构成链表。

建立链表

动态申请内存(malloc),按输入顺序将新节点尾插到链表中,构成学生信息链表。

遍历与输出

从头指针开始遍历链表即可输出所有学生信息。

修改功能

根据学号查找节点,选择要修改的成绩字段并更新其值。

计算平均成绩与输出

遍历链表计算每个学生的平均分并存入节点,再次遍历输出总分与平均分。

  1. 源程序清单:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

typedef struct student {

char id[20];

char name[20];

float eng, math, phy, cprog;

float avg;

struct student *next;

} Stu;

Stu* create(); //① 输入

void print(Stu *head); //② 输出

void modify(Stu *head);//③ 修改

void calcAvg(Stu *head);//④ 统计平均

void printAvg(Stu *head);//⑤ 输出总分和平均分

int menu();

int main() {

Stu *head = NULL;

int choice;

while(1) {

choice = menu();

switch(choice) {

case 1: head = create(); break;

case 2: print(head); break;

case 3: modify(head); break;

case 4: calcAvg(head); break;

case 5: printAvg(head); break;

case 0: exit(0);

default: printf("输入错误!\n");

}

}

return 0;

}

int menu() {

printf("\n===== 学生成绩管理系统 =====\n");

printf("1. 输入学生信息\n");

printf("2. 输出学生信息\n");

printf("3. 修改指定学生信息\n");

printf("4. 计算每位学生平均成绩\n");

printf("5. 输出总分与平均分\n");

printf("0. 退出\n");

printf("请选择:");

int c; scanf("%d", &c);

return c;

}

Stu* create() {

Stu *head = NULL, *p, *tail = NULL;

int n;

printf("请输入学生人数: ");

scanf("%d", &n);

for(int i = 0; i < n; i++) {

p = (Stu*)malloc(sizeof(Stu));

printf("输入 学号 姓名 英语 高数 物理 C语言 成绩:\n");

scanf("%s %s %f %f %f %f", p->id, p->name, &p->eng, &p->math, &p->phy, &p->cprog);

p->avg = 0;

p->next = NULL;

if(head == NULL) head = p;

else tail->next = p;

tail = p;

}

return head;

}

void print(Stu *head) {

Stu *p = head;

printf("\n学号\t姓名\t英语\t高数\t物理\tC语言\n");

while(p) {

printf("%s\t%s\t%.2f\t%.2f\t%.2f\t%.2f\n",

p->id, p->name, p->eng, p->math, p->phy, p->cprog);

p = p->next;

}

}

void modify(Stu *head) {

char id[20];

printf("输入要修改的学生学号: ");

scanf("%s", id);

Stu *p = head;

while(p && strcmp(p->id, id) != 0) p = p->next;

if(!p) { printf("未找到该学生!\n"); return; }

printf("选择要修改的科目:1英语 2高数 3物理 4C语言\n");

int c; scanf("%d", &c);

float score;

printf("输入新成绩: "); scanf("%f", &score);

if(c == 1) p->eng = score;

else if(c == 2) p->math = score;

else if(c == 3) p->phy = score;

else if(c == 4) p->cprog = score;

else printf("输入无效\n");

}

void calcAvg(Stu *head) {

Stu *p = head;

while(p) {

p->avg = (p->eng + p->math + p->phy + p->cprog) / 4.0;

p = p->next;

}

printf("平均分计算完成!\n");

}

void printAvg(Stu *head) {

Stu *p = head;

printf("\n学号\t姓名\t总分\t平均分\n");

while(p) {

float sum = p->eng + p->math + p->phy + p->cprog;

printf("%s\t%s\t%.2f\t%.2f\n", p->id, p->name, sum, p->avg);

p = p->next;

}

}

  1. 测试:
  1. 各项功能的运行截图:

图7-4程序设计题(1)的运行截图

(2)本关任务:对程序设计题第(1)题的程序,⑥增加按照平均成绩进行升序排序的函数,写出用交换结点数据域的方法升序排序的函数,排序可用选择法或冒泡法。

解答:

  1. 解题思路:

前提条件

平均成绩已计算完成(任务一的基础)。

排序策略

可使用冒泡法或选择法本质都是比较两个节点的 avg 值。

交换方式

不改变节点之间指针关系,而是直接交换结构体中的数据项(包括姓名、分数等字段)。

  1. 源程序清单:“

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

typedef struct student {

char id[20];

char name[20];

float eng, math, phy, cprog;

float avg;

struct student *next;

} Stu;

Stu* create(); //① 输入

void print(Stu *head); //② 输出

void modify(Stu *head);//③ 修改

void calcAvg(Stu *head);//④ 统计平均

void printAvg(Stu *head);//⑤ 输出总分和平均分

void sortDataSwap(Stu *head);

int menu();

int main() {

Stu *head = NULL;

int choice;

while(1) {

choice = menu();

switch(choice) {

case 1: head = create(); break;

case 2: print(head); break;

case 3: modify(head); break;

case 4: calcAvg(head); break;

case 5: printAvg(head); break;

case 6: sortDataSwap(head); break;

case 0: exit(0);

default: printf("输入错误!\n");

}

}

return 0;

}

int menu() {

printf("\n===== 学生成绩管理系统 =====\n");

printf("1. 输入学生信息\n");

printf("2. 输出学生信息\n");

printf("3. 修改指定学生信息\n");

printf("4. 计算每位学生平均成绩\n");

printf("5. 输出总分与平均分\n");

printf("6. 按平均成绩排序(交换数据域)\n");

printf("0. 退出\n");

printf("请选择:");

int c; scanf("%d", &c);

return c;

}

Stu* create() {

Stu *head = NULL, *p, *tail = NULL;

int n;

printf("请输入学生人数: ");

scanf("%d", &n);

for(int i = 0; i < n; i++) {

p = (Stu*)malloc(sizeof(Stu));

printf("输入 学号 姓名 英语 高数 物理 C语言 成绩:\n");

scanf("%s %s %f %f %f %f", p->id, p->name, &p->eng, &p->math, &p->phy, &p->cprog);

p->avg = 0;

p->next = NULL;

if(head == NULL) head = p;

else tail->next = p;

tail = p;

}

return head;

}

void print(Stu *head) {

Stu *p = head;

printf("\n学号\t姓名\t英语\t高数\t物理\tC语言\n");

while(p) {

printf("%s\t%s\t%.2f\t%.2f\t%.2f\t%.2f\n",

p->id, p->name, p->eng, p->math, p->phy, p->cprog);

p = p->next;

}

}

void modify(Stu *head) {

char id[20];

printf("输入要修改的学生学号: ");

scanf("%s", id);

Stu *p = head;

while(p && strcmp(p->id, id) != 0) p = p->next;

if(!p) { printf("未找到该学生!\n"); return; }

printf("选择要修改的科目:1英语 2高数 3物理 4C语言\n");

int c; scanf("%d", &c);

float score;

printf("输入新成绩: "); scanf("%f", &score);

if(c == 1) p->eng = score;

else if(c == 2) p->math = score;

else if(c == 3) p->phy = score;

else if(c == 4) p->cprog = score;

else printf("输入无效\n");

}

void calcAvg(Stu *head) {

Stu *p = head;

while(p) {

p->avg = (p->eng + p->math + p->phy + p->cprog) / 4.0;

p = p->next;

}

printf("平均分计算完成!\n");

}

void printAvg(Stu *head) {

Stu *p = head;

printf("\n学号\t姓名\t总分\t平均分\n");

while(p) {

float sum = p->eng + p->math + p->phy + p->cprog;

printf("%s\t%s\t%.2f\t%.2f\n", p->id, p->name, sum, p->avg);

p = p->next;

}

}

void sortDataSwap(Stu *head) {

Stu *p, *q;

Stu temp;

for(p = head; p && p->next; p = p->next) {

for(q = p->next; q; q = q->next) {

if(p->avg > q->avg) { // 升序

temp = *p; *p = *q; *q = temp;

// 修正 next 指针被交换的问题

Stu *t = p->next;

p->next = q->next;

q->next = t;

}

}

}

printf("已按平均成绩升序排序(数据域交换)!\n");

}

  1. 测试:
  1. 各项功能的运行截图:

图7-5程序设计题(2)的运行截图

  1. 本关任务:对程序设计题第(2)题,进一步写出用交换结点指针域的方法升序排序的函数。
  1. 解题思路:

指针操作代替数据交换

找出当前循环中平均分最小的节点,用指针操作将其移动到正确位置。

排序方法

通常采用选择排序思想:每轮从剩余链表中找出最小 avg 节点,并将指针调整到指定位置

  1. 源程序清单:

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

typedefstructstudent{

charid[20];

charname[20];

floateng, math, phy, cprog;

floatavg;

structstudent* next;

}Stu;

Stu* create();//输入

voidprint(Stu*head);//输出

voidmodify(Stu*head);//修改

voidcalcAvg(Stu*head);//统计平均

voidprintAvg(Stu*head);//输出总分和平均分

voidsortDataSwap(Stu*head);

Stu* sortPointerSwap(Stu*head);

intmenu();

intmain() {

Stu* head =NULL;

intchoice;

while(1) {

choice = menu();

switch(choice) {

case1: head = create();break;

case2: print(head);break;

case3: modify(head);break;

case4: calcAvg(head);break;

case5: printAvg(head);break;

case6: sortDataSwap(head);break;

case7: head = sortPointerSwap(head);break;

case0: exit(0);

default: printf("输入错误!\n");

}

}

return0;

}

intmenu() {

printf("\n=====学生成绩管理系统=====\n");

printf("1.输入学生信息\n");

printf("2.输出学生信息\n");

printf("3.修改指定学生信息\n");

printf("4.计算每位学生平均成绩\n");

printf("5.输出总分与平均分\n");

printf("6.按平均成绩排序(交换数据域)\n");

printf("7.按平均成绩排序(交换指针域)\n");

printf("0.退出\n");

printf("请选择:");

intc; scanf("%d", &c);

returnc;

}

Stu* create() {

Stu* head =NULL, * p, * tail =NULL;

intn;

printf("请输入学生人数: ");

scanf("%d", &n);

for(inti = 0; i < n; i++) {

p = (Stu*)malloc(sizeof(Stu));

printf("输入学号姓名英语高数物理C语言成绩:\n");

scanf("%s %s %f %f %f %f", p->id, p->name, &p->eng, &p->math, &p->phy, &p->cprog);

p->avg = 0;

p->next =NULL;

if(head ==NULL) head = p;

elsetail->next = p;

tail = p;

}

returnhead;

}

voidprint(Stu*head) {

Stu* p =head;

printf("\n学号\t姓名\t英语\t高数\t物理\tC语言\n");

while(p) {

printf("%s\t%s\t%.2f\t%.2f\t%.2f\t%.2f\n",

p->id, p->name, p->eng, p->math, p->phy, p->cprog);

p = p->next;

}

}

voidmodify(Stu*head) {

charid[20];

printf("输入要修改的学生学号: ");

scanf("%s", id);

Stu* p =head;

while(p && strcmp(p->id, id) != 0) p = p->next;

if(!p) { printf("未找到该学生!\n");return; }

printf("选择要修改的科目:1英语2高数3物理4C语言\n");

intc; scanf("%d", &c);

floatscore;

printf("输入新成绩: "); scanf("%f", &score);

if(c == 1) p->eng = score;

elseif(c == 2) p->math = score;

elseif(c == 3) p->phy = score;

elseif(c == 4) p->cprog = score;

elseprintf("输入无效\n");

}

voidcalcAvg(Stu*head) {

Stu* p =head;

while(p) {

p->avg = (p->eng + p->math + p->phy + p->cprog) / 4.0;

p = p->next;

}

printf("平均分计算完成!\n");

}

voidprintAvg(Stu*head) {

Stu* p =head;

printf("\n学号\t姓名\t总分\t平均分\n");

while(p) {

floatsum = p->eng + p->math + p->phy + p->cprog;

printf("%s\t%s\t%.2f\t%.2f\n", p->id, p->name, sum, p->avg);

p = p->next;

}

}

voidsortDataSwap(Stu*head) {

Stu* p, * q;

Stutemp;

for(p =head; p && p->next; p = p->next) {

for(q = p->next; q; q = q->next) {

if(p->avg > q->avg) {//升序

temp = *p; *p = *q; *q = temp;

//修正next指针被交换的问题

Stu* t = p->next;

p->next = q->next;

q->next = t;

}

}

}

printf("已按平均成绩升序排序(数据域交换)!\n");

}

Stu* sortPointerSwap(Stu*head) {

if(!head)returnhead;

Studummy, * p, * q, * minPrev, * prev;

dummy.next =head;

prev = &dummy;

while(prev->next) {

minPrev = prev;

q = prev->next;

while(q->next) {

if(q->next->avg < minPrev->next->avg)

minPrev = q;

q = q->next;

}

if(minPrev != prev) {//交换节点指针

Stu* minNode = minPrev->next;

minPrev->next = minNode->next;

minNode->next = prev->next;

prev->next = minNode;

}

prev = prev->next;

}

printf("已按平均成绩升序排序(指针域交换)!\n");

returndummy.next;

}

  1. 测试:
  1. 各项功能的运行截图:

图7-6程序设计题(3)的运行截图

7.3实验小结

通过本次链表成绩管理系统的实现,我不仅掌握了单向链表的建立、遍历和修改等基本操作,还深入理解了结构体在数据组织中的作用;在完成平均成绩计算和排序功能的过程中,我体会到不同排序方法在链表结构中的实现差异,尤其是交换数据域与交换指针域的逻辑思想,使我对链表在动态数据管理中的优势有了更加直观而深刻的认识,也提升了我分析问题和模块化设计程序的能力。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 5:34:52

5大信息获取神器深度评测:打破知识壁垒的终极方案

在信息爆炸的时代&#xff0c;你是否经常遇到这样的困境&#xff1a;精彩的深度报道被付费墙阻挡&#xff0c;重要的学术论文需要高价订阅&#xff0c;优质的内容资源难以触达&#xff1f;掌握正确的信息获取工具已经成为现代人必备的数字技能。本文将为你揭秘5款高效的内容获取…

作者头像 李华
网站建设 2026/4/17 5:39:11

AI仓储管理系统:技术重构仓储效率,产品视角下的硬核赋能

做仓储相关产品久了&#xff0c;最懂传统仓储的痛点&#xff1a;货物堆成山&#xff0c;人工找货像“大海捞针”&#xff1b;出入库全靠纸笔核对&#xff0c;错发、漏发是常事&#xff1b;库存多少全凭经验猜&#xff0c;要么缺货耽误订单&#xff0c;要么积压占用资金。这些问…

作者头像 李华
网站建设 2026/4/18 7:59:05

AI如何自动诊断并修复‘连接被拒绝‘错误

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个Python脚本&#xff0c;使用AI自动诊断连接被拒绝错误。脚本应能&#xff1a;1. 检查目标URL/端口的可达性 2. 分析常见原因(防火墙、服务未启动等) 3. 根据错误类型提供修…

作者头像 李华
网站建设 2026/4/18 9:25:43

WebClient入门指南:从零开始学网络请求

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 编写一个入门级WebClient教程项目&#xff0c;包含&#xff1a;1. 最基本的GET请求示例 2. POST请求发送表单数据 3. 处理文本和JSON响应 4. 文件下载功能 5. 进度回调实现。使用C#…

作者头像 李华
网站建设 2026/4/18 8:01:22

破壁异构计算 - Ascend C在CANN全栈中的战略支点角色

目录 摘要 1. 异构计算的“巴别塔困境”与CANN的破局之道 1.1 从硬件算力到应用效能的鸿沟 1.2 CANN的全栈视角&#xff1a;不只是“驱动程序” 2. Ascend C架构设计&#xff1a;达芬奇架构的“精准映射” 2.1 硬件抽象层的设计哲学 2.2 三级存储体系的最佳实践 3. 核心…

作者头像 李华
网站建设 2026/4/18 1:06:07

【Java毕设全套源码+文档】基于springboot的甘肃“印象”网站设计与实现(丰富项目+远程调试+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华