一,前言
在我们学习了操作符,选择语句,循环语句,函数以及数组等这些必不可少的知识。那么你是都会好奇,到底学到哪里才算是一个阶段。那我今天就告诉你,C语言结构体学完这个,你就算是把这个初阶的知识学完了。你想的没有错,本篇写完C语言初阶就完结了(后续会更新进阶)。感谢各位大佬的支持,从刚开始写博客没想到会坚持到现在,心情真的很复杂。胡不多说,C语言结构体开讲!!!(本篇50%内容借助ai生成)
一,结构体(struct)
1,结构体的介绍
在C语言中,结构体(struct) 是一种自定义的复合数据类型,允许将多个不同类型的变量组合成一个逻辑单元,类似于现实世界中对象的属性集合。它是C语言实现数据封装和抽象的核心机制之一。
2,结构体定义
在C语言中,结构体也是非常重要的一个部分,想要学好结构体,那么先从定义开始!
struct Student
{
char name[50]; //姓名
int age; //年龄
float score; //分数
char gender; //性别
};
3.用定义去描述一件事物
假如我想要用结构体描述一本书,我想知道它的价格,多少页,名字是什么?
struct Book
{
char BookName[30]; //书的名字
double Money; //多少钱
int Page; //页数
char author //作者
}; //分号不可省略
二,实例化结构体变量
1,直接声明
struct Book myBook; // 声明一个book类型的变量
2,使用typedef简化
typedef struct
{
char title[100];
char author[50];
int pages;
float price;
} Book;
三,结构体中如何访问成员
#include <stdio.h>
#include <string.h>
// 定义书籍结构体
typedef struct {
char title[100]; // 书名
char author[50]; // 作者
int pages; // 页数
float price; // 价格
} Book;
// 打印书籍信息函数
void print_book_info(const Book* book) {
printf("===== 书籍信息 =====\n"
"书名:%s\n"
"作者:%s\n"
"页数:%d页\n"
"价格:%.2f元\n",
book->title, book->author, book->pages, book->price);
}
int main() {
// 直接初始化结构体变量
Book myBook = {
.title = "结构体指南",
.author = "Hou'",
.pages = 99,
.price = 0.00
};
print_book_info(&myBook);
return 0;
}
四, 内存布局与对齐规则
1,结构体内存布局基本特征
成员按照声明顺序依次存储 |
每个成员的起始地址都必须满足其类型的对其要求 |
结构体总大小必须是最大成员对齐值的整数倍 |
2. 内存布局示意图
struct Example
{
char a; // 1字节
int b; // 4字节
short c; // 2字节
double d; // 8字节
};
内存布局(假设8字节对齐)
| a | 填充3字节 | b (4字节) | c | 填充6字节 | d (8字节) |
| 0 1 2 3 | 4 5 6 7 | 8 9 | 10-15 | 16-23 |
3、对齐规则详解
3.1.对齐系数
基本类型对齐值 = 类型大小
char : 1字节
short : 2字节
int : 4字节
double : 8字节
结构体对齐值 = 类型大小
2. 偏移量计算规则
注:每个成员的编译地址必须满足:
offset % alignment == 0
3. 结构体总大小计算
计算累计代下(包含填充字节)
最终大小必须时最大对齐值得整数倍
四、内存对齐的本质原因 (仅做了解)
硬件优化 | 缓存行优化 | 原子操作支持 |
CPU访问对齐内存效率更高 | 现在CPU缓存行通常为64字节 | 原子操作必须对齐(看情况) |
某些架构无法直接访问非对齐内存 | 对齐数据可以最大化缓存利用率 |
五,结构体的优势
1. 数据封装与逻辑组织
优势:将多个相关数据项组合为单一实体,形成有意义的抽象数据类型。
示例:管理学生信息时,无需使用多个独立变量,而是组合成一个逻辑单元。
// 未使用结构体(松散变量)
char studentName[50];
int studentAge;
float studentGPA;
char studentMajor[30];
// 使用结构体(封装数据)
struct Student
{
char name[50];
int age;
float gpa;
char major[30];
};
2. 简化函数参数传递
优势:通过传递结构体指针,减少函数参数数量,提升代码可维护性。
// 未使用结构体:需传递多个参数
void printStudent(char *name, int age, float gpa)
{
printf("Name: %s, Age: %d, GPA: %.2f\n", name, age, gpa);
}
// 使用结构体:只需一个指针参数
void printStudent(struct Student *s)
{
printf("Name: %s, Age: %d, GPA: %.2f\n", s->name, s->age, s->gpa);
}
3. 高效内存管理
优势:结构体成员在内存中连续存储,提升缓存利用率,适合批量处理数据。
// 创建结构体数组
struct Student class[50];
// 连续访问内存(高效率)
for (int i = 0; i < 50; i++)
{
class[i].age = 18 + i;
}
对比松散变量:
int ages[50];
char names[50][50];
// 内存不连续,缓存命中率低
4. 增强代码可读性与维护性
优势:通过有意义的类型命名,使代码自文档化。
// 未使用结构体(含义不明确)
void processData(int x, int y, int width, int height);
// 使用结构体(语义清晰)
struct Rectangle
{
int x;
int y;
int width;
int height;
};
void processRect(struct Rectangle *rect);
六,结构体高级用法
1. 动态内存分配
typedef struct TreeNode
{
int data;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
// 创建树节点
TreeNode* createNode(int val)
{
TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
if(newNode)
{
newNode->data = val;
newNode->left = newNode->right = NULL;
}
return newNode;
}
// 插入二叉搜索树
void insertBST(TreeNode** root, int val)
{
if(!*root)
{
*root = createNode(val);
return;
}
if(val < (*root)->data)
insertBST(&(*root)->left, val);
else
insertBST(&(*root)->right, val);
}
2. 自引用结构体(链表)
struct ListNode
{
int val;
struct ListNode* next;
struct ListNode* random; // 复杂链表
};
// 深拷贝带随机指针的链表
struct ListNode* copyRandomList(struct ListNode* head)
{
// ...实现省略...
}
3. 实现类面向对象编程
typedef struct
{
int x, y;
void (*draw)(void* self); // 函数指针
void (*move)(void* self, int dx, int dy);
} Shape;
// 圆形实现
void circleDraw(void* self)
{
Shape* s = (Shape*)self;
printf("Drawing circle at (%d,%d)\n", s->x, s->y);
}
void circleMove(void* self, int dx, int dy)
{
Shape* s = (Shape*)self;
s->x += dx;
s->y += dy;
}
// 创建对象
Shape createCircle(int x, int y)
{
return (Shape){x, y, circleDraw, circleMove};
}
// 使用
Shape c = createCircle(10,20);
c.draw(&c);
c.move(&c,5,5);
七,结构体与函数
1. 值传递 vs 指针传递
typedef struct
{
int x;
int y;
} Point;
// 值传递(复制整个结构体)
void movePointVal(Point p, int dx, int dy)
{
p.x += dx;
p.y += dy; // 修改不影响原结构体
}
// 指针传递(推荐方式)
void movePointPtr(Point* p, int dx, int dy)
{
p->x += dx;
p->y += dy; // 直接影响原结构体
}
int main() {
Point pt = {10, 20};
movePointVal(pt, 5, 5); // pt仍为(10,20)
movePointPtr(&pt, 5, 5); // pt变为(15,25)
return 0;
}
八,学生管理系统
学生管理系统得实现和功能
1,数据结构:
使用链表结构存储学生数据
学生信息包含:学号、姓名、年龄、成绩
2,主要功能:
添加学生信息(自动检测学号重复)
删除学生信息(按学号)
修改学生信息(按学号)
查询学生(支持学号/姓名查询)
显示所有学生信息(表格形式)
数据持久化(二进制文件存储)
3,特色功能:
输入合法性检测
内存自动管理
友好的用户界面
数据文件自动加载/保存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_NAME_LEN 50
#define MAX_ID_LEN 15
#define DATA_FILE "students.dat"
typedef struct Student
{
char id[MAX_ID_LEN]; // 学号
char name[MAX_NAME_LEN]; // 姓名
int age; // 年龄
float score; // 成绩
struct Student *next; // 链表指针
} Student;
Student *head = NULL; // 全局链表头指针
/* 函数声明 */
void display_menu();
void add_student();
void delete_student();
void modify_student();
void search_student();
void display_all();
void save_to_file();
void load_from_file();
void clear_list();
int is_id_exist(const char *id);
Student *create_node();
void press_enter_to_continue();
int main()
{
load_from_file();
int choice;
do {
display_menu();
printf("请输入选项:");
scanf("%d", &choice);
while(getchar() != '\n'); // 清空输入缓冲区
switch(choice)
{
case 1: add_student(); break;
case 2: delete_student(); break;
case 3: modify_student(); break;
case 4: search_student(); break;
case 5: display_all(); break;
case 6: save_to_file(); break;
case 0: printf("感谢使用!\n"); break;
default: printf("无效选项,请重新输入!\n");
}
} while(choice != 0);
clear_list();
return 0;
}
void display_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("=========================\n");
}
Student *create_node()
{
Student *new_node = (Student *)malloc(sizeof(Student));
if(new_node == NULL)
{
printf("内存分配失败!\n");
exit(EXIT_FAILURE);
}
new_node->next = NULL;
return new_node;
}
void add_student()
{
Student *new_node = create_node();
printf("\n--- 添加学生 ---\n");
do {
printf("请输入学号:");
fgets(new_node->id, MAX_ID_LEN, stdin);
new_node->id[strcspn(new_node->id, "\n")] = '\0'; // 去除换行符
} while(is_id_exist(new_node->id));
printf("请输入姓名:");
fgets(new_node->name, MAX_NAME_LEN, stdin);
new_node->name[strcspn(new_node->name, "\n")] = '\0';
printf("请输入年龄:");
scanf("%d", &new_node->age);
printf("请输入成绩:");
scanf("%f", &new_node->age);
// 头插法添加到链表
new_node->next = head;
head = new_node;
printf("添加成功!\n");
press_enter_to_continue();
}
void delete_student()
{
if(head == NULL)
{
printf("当前没有学生记录!\n");
return;
}
char target_id[MAX_ID_LEN];
printf("\n--- 删除学生 ---\n");
printf("请输入要删除的学号:");
fgets(target_id, MAX_ID_LEN, stdin);
target_id[strcspn(target_id, "\n")] = '\0';
Student *current = head, *prev = NULL;
while(current != NULL)
{
if(strcmp(current->id, target_id) == 0)
{
if(prev == NULL)
{
head = current->next;
} else
{
prev->next = current->next;
}
free(current);
printf("删除成功!\n");
press_enter_to_continue();
return;
}
prev = current;
current = current->next;
}
printf("未找到该学号的学生!\n");
press_enter_to_continue();
}
void modify_student()
{
if(head == NULL)
{
printf("当前没有学生记录!\n");
return;
}
char target_id[MAX_ID_LEN];
printf("\n--- 修改信息 ---\n");
printf("请输入要修改的学号:");
fgets(target_id, MAX_ID_LEN, stdin);
target_id[strcspn(target_id, "\n")] = '\0';
Student *current = head;
while(current != NULL)
{
if(strcmp(current->id, target_id) == 0) {
printf("原姓名:%s\n新姓名:", current->name);
fgets(current->name, MAX_NAME_LEN, stdin);
current->name[strcspn(current->name, "\n")] = '\0';
printf("原年龄:%d\n新年龄:", current->age);
scanf("%d", ¤t->age);
printf("原成绩:%.1f\n新成绩:", current->score);
scanf("%f", ¤t->score);
printf("修改成功!\n");
press_enter_to_continue();
return;
}
current = current->next;
}
printf("未找到该学号的学生!\n");
press_enter_to_continue();
}
void search_student()
{
if(head == NULL)
{
printf("当前没有学生记录!\n");
return;
}
char target[MAX_ID_LEN];
printf("\n--- 查询学生 ---\n");
printf("请输入学号或姓名:");
fgets(target, MAX_ID_LEN, stdin);
target[strcspn(target, "\n")] = '\0';
Student *current = head;
int found = 0;
while(current != NULL)
{
if(strcmp(current->id, target) == 0 ||
strcmp(current->name, target) == 0)
{
printf("\n学号:%s\n", current->id);
printf("姓名:%s\n", current->name);
printf("年龄:%d\n", current->age);
printf("成绩:%.1f\n\n", current->score);
found = 1;
}
current = current->next;
}
if(!found)
{
printf("未找到匹配的学生!\n");
}
press_enter_to_continue();
}
void display_all()
{
if(head == NULL)
{
printf("当前没有学生记录!\n");
return;
}
printf("\n%-15s%-20s%-8s%-8s\n",
"学号", "姓名", "年龄", "成绩");
printf("----------------------------------------\n");
Student *current = head;
while(current != NULL)
{
printf("%-15s%-20s%-8d%-8.1f\n",
current->id,
current->name,
current->age,
current->score);
current = current->next;
}
press_enter_to_continue();
}
void save_to_file()
{
FILE *fp = fopen(DATA_FILE, "wb");
if(fp == NULL)
{
printf("无法打开文件进行保存!\n");
return;
}
Student *current = head;
while(current != NULL)
{
fwrite(current, sizeof(Student), 1, fp);
current = current->next;
}
fclose(fp);
printf("数据已保存到%s\n", DATA_FILE);
press_enter_to_continue();
}
void load_from_file()
{
FILE *fp = fopen(DATA_FILE, "rb");
if(fp == NULL) return;
Student temp, *new_node;
while(fread(&temp, sizeof(Student), 1, fp) == 1)
{
new_node = create_node();
memcpy(new_node, &temp, sizeof(Student));
new_node->next = head;
head = new_node;
}
fclose(fp);
}
void clear_list()
{
Student *current = head;
while(current != NULL)
{
Student *temp = current;
current = current->next;
free(temp);
}
head = NULL;
}
int is_id_exist(const char *id)
{
Student *current = head;
while(current != NULL)
{
if(strcmp(current->id, id) == 0)
{
printf("该学号已存在!\n");
return 1;
}
current = current->next;
}
return 0;
}
void press_enter_to_continue()
{
printf("\n按回车键继续...");
while(getchar() != '\n');
getchar(); // 等待回车
}
十,结构体
以下是一份系统完整的C语言结构体综合示例代码,涵盖结构体核心用法、内存管理、高级特性及实际应用
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h> // 用于offsetof宏
/*-----------------------------------------
第一部分:结构体基础与内存布局
-----------------------------------------*/
// 基础结构体定义
typedef struct
{
char id[10];
int age;
double score;
} Student;
// 带内存对齐控制的结构体
#pragma pack(push, 1) // 1字节对齐
typedef struct
{
char type;
int code;
short flag;
} PackedData;
#pragma pack(pop) // 恢复默认对齐
void basic_demo()
{
printf("\n=== 结构体基础与内存布局演示 ===\n");
Student s1 = {"A1001", 20, 85.5};
printf("学生 %s 年龄:%d 分数:%.1f\n", s1.id, s1.age, s1.score);
printf("默认对齐结构体大小:%zu\n", sizeof(Student));
printf("紧凑对齐结构体大小:%zu\n", sizeof(PackedData));
}
/*-----------------------------------------
第二部分:结构体高级特性
-----------------------------------------*/
// 位域结构体
typedef struct
{
unsigned int is_active : 1;
unsigned int mode : 3;
unsigned int : 4; // 未命名位域
unsigned int value : 8;
} StatusRegister;
// 包含函数指针的结构体
typedef struct
{
double x, y;
void (*print)(const struct Vector*);
} Vector;
void vector_print(const Vector* v)
{
printf("(%.2f, %.2f)\n", v->x, v->y);
}
// 柔性数组成员
typedef struct
{
size_t length;
int data[];
} IntArray;
IntArray* create_int_array(size_t len)
{
IntArray* arr = malloc(sizeof(IntArray) + len * sizeof(int));
if(arr)
{
arr->length = len;
for(size_t i=0; i<len; i++)
arr->data[i] = (int)(i+1);
}
return arr;
}
void advanced_demo()
{
printf("\n=== 高级特性演示 ===\n");
// 位域操作
StatusRegister reg = {1, 5, 128};
printf("状态寄存器:active=%d mode=%d value=%d\n",
reg.is_active, reg.mode, reg.value);
// 函数指针成员
Vector v = {3.5, 4.2, vector_print};
printf("向量坐标:");
v.print(&v);
// 柔性数组使用
IntArray* arr = create_int_array(5);
printf("动态数组:");
for(size_t i=0; i<arr->length; i++)
printf("%d ", arr->data[i]);
free(arr);
}
/*-----------------------------------------
第三部分:结构体与函数
-----------------------------------------*/
// 结构体作为函数参数
void update_student(Student* s, int new_age)
{
s->age = new_age;
s->score *= 1.1;
}
// 返回结构体的函数
Student create_student(const char* id, int age)
{
Student s;
strncpy(s.id, id, sizeof(s.id)-1);
s.age = age;
s.score = 60.0;
return s;
}
// 回调函数示例
typedef struct
{
int value;
void (*callback)(int);
} Event;
void event_handler(int val)
{
printf("事件触发,值:%d\n", val);
}
void function_demo() {
printf("\n=== 结构体与函数交互演示 ===\n");
Student s2 = create_student("B2002", 19);
update_student(&s2, 20);
printf("更新后学生:%s 年龄:%d\n", s2.id, s2.age);
Event e = {100, event_handler};
if(e.callback) e.callback(e.value);
}
/*-----------------------------------------
第四部分:链表数据结构实现
-----------------------------------------*/
typedef struct Node {
int data;
struct Node* next;
} ListNode;
ListNode* create_list(int values[], int n) {
ListNode head = {0, NULL};
ListNode* current = &head;
for(int i=0; i<n; i++) {
current->next = malloc(sizeof(ListNode));
current = current->next;
current->data = values[i];
current->next = NULL;
}
return head.next;
}
void list_demo()
{
printf("\n=== 链表结构演示 ===\n");
int values[] = {10, 20, 30, 40};
ListNode* list = create_list(values, 4);
ListNode* current = list;
while(current)
{
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
// 内存释放(实际开发需实现)
}
/*-----------------------------------------
第五部分:内存对齐验证
-----------------------------------------*/
void alignment_check()
{
printf("\n=== 内存对齐验证 ===\n");
printf("Student结构体成员偏移:\n");
printf("id偏移:%zu\n", offsetof(Student, id));
printf("age偏移:%zu\n", offsetof(Student, age));
printf("score偏移:%zu\n", offsetof(Student, score));
// 静态断言(C11)
_Static_assert(sizeof(Student) == 24, "Student结构体大小异常");
}
int main()
{
basic_demo();
advanced_demo();
function_demo();
list_demo();
alignment_check();
return 0;
}