目录
一、引言
二、结构体基础
2.1 结构体定义
2.2 结构体变量的声明与初始化
三、结构体对齐
3.1 结构体对齐的概念
3.2 影响结构体对齐的因素
四、链表实现
4.1 链表的基本概念
4.2 单向链表的实现
五、学生管理系统升级版实现
5.1 系统需求升级
在 C 语言编程中,处理复杂数据时,简单的数据类型往往捉襟见肘。结构体作为一种复合数据类型,允许将不同类型的数据组合在一起,形成一个有机的整体。而链表作为一种基于结构体的动态数据结构,能有效解决数组在数据存储和管理上的局限性。本章将深入探讨结构体的定义、使用,特别是结构体对齐这一关键概念,同时详细介绍链表的实现,最后通过打造一个升级版的学生管理系统,综合运用这些知识,提升程序的灵活性与实用性。
结构体定义了一种新的数据类型,它将多个不同类型的变量组织在一起。其定义格式如下:
struct structure_name {
data_type member1;
data_type member2;
// 更多成员
};
例如,定义一个表示学生信息的结构体:
#include
struct Student {
char name[50];
int age;
float grade;
};
在上述代码中,struct Student
定义了一个新的数据类型,包含 name
(字符数组)- 名字、age
(整型)- 年龄 和 grade
(浮点型)- 评分三个成员。
定义结构体后,就可以声明结构体变量并进行初始化。
int main() {
// 声明结构体变量
struct Student student1;
// 初始化结构体变量
strcpy(student1.name, "Alice");
student1.age = 20;
student1.grade = 3.5;
printf("Student Name: %s\n", student1.name);
printf("Student Age: %d\n", student1.age);
printf("Student Grade: %.2f\n", student1.grade);
return 0;
}
当然也可以在声明变量时直接初始化:
struct Student student2 = {"Bob", 21, 3.8};
结构体对齐是指编译器为结构体成员分配内存时,会在成员之间插入一些填充字节,以确保每个成员都位于特定的内存地址边界上。这主要是为了提高内存访问效率,因为现代计算机硬件在访问特定对齐地址的数据时性能更高。
char
类型通常按 1 字节对齐,int
类型按 4 字节对齐,double
类型按 8 字节对齐。double
(8 字节对齐),则结构体的总大小会是 8 的整数倍。3.3 示例代码分析
#include
struct Example1 {
char c;
int i;
double d;
};
struct Example2 {
int i;
char c;
double d;
};
int main() {
printf("Size of Example1: %zu\n", sizeof(struct Example1));
printf("Size of Example2: %zu\n", sizeof(struct Example2));
return 0;
}
运行结果:
在上述代码中,struct Example1
中 char
占 1 字节,int
占 4 字节,double
占 8 字节。由于 int
按 4 字节对齐,char
后会填充 3 字节,int
与 double
之间填充 4 字节,所以 struct Example1
的总大小为 16 字节。而 struct Example2
中 int
先占 4 字节,char
占 1 字节后填充 7 字节,所以总大小为 24 字节。
链表是一种动态数据结构,由一系列节点组成。每个节点包含两部分:数据部分和指针部分。指针部分指向下一个节点,通过这种方式将所有节点连接成一个链式结构。链表分为单向链表、双向链表和循环链表,这里主要介绍单向链表。
1.定义链表节点:
struct ListNode {
int data;
struct ListNode *next;
};
在这个结构体定义中,我们定义了一个链表节点。每个节点包含两部分:
int data
:存储节点的数据。struct ListNode *next
:指向下一个节点的指针。2.创建新节点:
struct ListNode* createNode(int value) {
struct ListNode *newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
newNode->data = value;
newNode->next = NULL;
return newNode;
}
这个函数用于创建一个新的链表节点:
malloc
分配内存空间,大小为 struct ListNode
结构体的大小。value
。NULL
,表示它目前没有指向任何其他节点。3.插入节点到链表头部:
struct ListNode* insertAtHead(struct ListNode *head, int value) {
struct ListNode *newNode = createNode(value);
newNode->next = head;
return newNode;
}
这个函数用于在链表的头部插入一个新节点:
createNode
函数创建一个新节点。next
指针指向当前的头节点 head
。4.遍历链表:
void traverseList(struct ListNode *head) {
struct ListNode *current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
这个函数用于遍历链表并打印每个节点的数据:
current
来遍历链表,初始时指向头节点 head
。while
循环中,打印当前节点的数据,然后将 current
移动到下一个节点。current
为 NULL
时,表示已经到达链表的末尾,此时打印 "NULL" 并结束循环5.完整示例:
#include
#include
struct ListNode {
int data;
struct ListNode *next;
};
struct ListNode* createNode(int value) {
struct ListNode *newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
newNode->data = value;
newNode->next = NULL;
return newNode;
}
struct ListNode* insertAtHead(struct ListNode *head, int value) {
struct ListNode *newNode = createNode(value);
newNode->next = head;
return newNode;
}
void traverseList(struct ListNode *head) {
struct ListNode *current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
int main() {
struct ListNode *head = NULL;
head = insertAtHead(head, 3);
head = insertAtHead(head, 2);
head = insertAtHead(head, 1);
traverseList(head);
return 0;
}
运行结果:
在 main
函数中:
head
为 NULL
,表示链表为空。insertAtHead
函数三次,在链表头部依次插入数据 3
、2
和 1
。插入后,链表顺序为 1 -> 2 -> 3
。traverseList
函数遍历并打印链表。
在之前学生成绩管理系统的基础上,增加以下功能:
#include
#include
#include
#define MAX_NAME_LEN 20
struct Student {
char name[MAX_NAME_LEN];
int id;
float score;
};
// 添加学生到数组
void addStudent(struct Student *stuList, int *count) {
if (*count >= 100) {
printf("人数已满!\n");
return;
}
printf("输入姓名 学号 分数:");
scanf("%s %d %f", stuList[*count].name, &stuList[*count].id, &stuList[*count].score);
(*count)++;
}
// 按学号查找学生
void findStudent(struct Student *stuList, int count, int targetId) {
for (int i=0; i
优化预告:下一章将结合文件操作实现数据持久化存储!
挑战任务:
实现链表删除指定值的节点(考虑头节点、中间节点、尾节点情况),并处理空链表和值不存在的场景。
下一篇预告:第八章《文件操作》——完成学生管理系统数据存储,实现跨程序数据持久化!
投票题目:
以下结构体的总大小是多少?(32位系统,默认对齐)
struct Data {
char c;
int i;
short s;
};