更新时间:作者:小小条
把数据打包在一起,为链表做准备
症状:
指针好懵逼链表要炸了看不懂代码原因: 没搞懂结构体!

结论: 先理解结构体,链表就不难了!
从控制台输入10个学生的信息:
姓名年龄性别如何保存?
#include <stdio.h>int main() {char names[10][20]; // 10个学生的姓名int ages[10]; // 10个学生的年龄char genders[10]; // 10个学生的性别 // 输入for (int i = 0; i < 10; i++) {printf("请输入第%d个学生的信息:\n", i + 1);printf("姓名:");scanf("%s", names[i]);printf("年龄:");scanf("%d", &ages[i]);printf("性别(M/F):");scanf(" %c", &genders[i]); }return 0;}
缺点:
数据分散关联性弱操作繁琐扩展困难结构体 = 把不同类型的数据组合在一起
就像一个盒子,装各种东西!
struct 结构体名 { 数据类型1 成员名1; 数据类型2 成员名2; 数据类型3 成员名3; ...};
注意末尾的分号!
struct Student {char name[20]; // 姓名int age; // 年龄char gender; // 性别};
含义:
定义了一个名为 Student 的结构体类型包含三个成员:name、age、genderstruct Student s1; // 声明一个Student类型的变量
注意: 每次都要写 struct 关键字
每次声明都要写 struct:
struct Student s1;struct Student s2;struct Student s3;
太麻烦了!
使用 typedef 重定义类型名:
typedef struct {char name[20];int age;char gender;} Student;
现在可以这样声明:
Student s1; // 不用写 struct 了!Student s2;Student s3;
typedef + 匿名结构体:
结构体本身没有名字直接定义别名 Student使用更简洁typedef struct {char name[20]; // 20字节int age; // 4字节char gender; // 1字节} Student;Student s1;
内存分配:
┌──────────────────────┬─────────┬────┐│ name[20] │ age │gen ││ 20字节 │ 4字节 │1字节│└──────────────────────┴─────────┴────┘
总共应该是: 20 + 4 + 1 = 25字节
但实际是: 28字节!❓
规则1:成员起始位置对齐
1字节类型:任意地址2字节类型:2的整数倍地址4字节类型:4的整数倍地址8字节类型:8的整数倍地址规则2:结构体大小对齐
结构体总大小必须是最大成员类型的整数倍typedef struct {char name[20]; // 20字节int age; // 4字节char gender; // 1字节} Student;
内存分配:
地址: 0 20 24 25 ┌──────┬────┬─┬───┐ │name │age │g│填充│ │20字节│4字节│1│3字节│ └──────┴────┴─┴───┘ 20 4 1 3总计:28字节(4的整数倍)
为什么是28字节?
name:20字节(0-19地址)age:4字节(20-23地址,4字节对齐)gender:1字节(24地址)填充:3字节(25-27地址)总大小:28字节(最大成员int是4字节,28是4的倍数)1. CPU访问效率
CPU一次读取4字节或8字节对齐后一次就能读取完整数据未对齐可能需要多次读取2. 硬件要求
某些硬件要求数据对齐未对齐会导致错误不同顺序,不同大小:
方案1:
typedef struct {char name[20]; // 20字节int age; // 4字节char gender; // 1字节} Student; // 28字节
方案2:
typedef struct {char name[20]; // 20字节char gender; // 1字节int age; // 4字节} Student; // 28字节
方案3(优化):
typedef struct {int age; // 4字节char name[20]; // 20字节char gender; // 1字节} Student; // 28字节
把最大类型放前面:
确定最大类型(如int = 4字节)按最大宽度排列成员尽量紧凑排列示例:
typedef struct {double value; // 8字节int count; // 4字节char flag; // 1字节} Data; // 16字节(8的倍数)
#pragma pack(1) // 强制1字节对齐typedef struct {char name[20];int age;char gender;} Student; // 25字节#pragma pack() // 恢复默认对齐
原因:
访问速度变慢CPU效率降低某些平台不支持只在特殊场合使用:
网络协议文件格式硬件接口访问结构体成员:
typedef struct {char name[20];int age;char gender;} Student;int main() {Student s1; // 赋值strcpy(s1.name, "张三");s1.age = 18;s1.gender = 'M'; // 访问printf("姓名:%s\n", s1.name);printf("年龄:%d\n", s1.age);printf("性别:%c\n", s1.gender);return 0;}
通过指针访问:
int main() {Student s1;Student *p = &s1; // 方式1:通过指针和点运算符 (*p).age = 18; // 方式2:通过箭头运算符(推荐)p->age = 18;p->gender = 'M';strcpy(p->name, "张三");printf("%s, %d岁, %c\n", p->name, p->age, p->gender);return 0;}
等价关系:
p->age ≡ (*p).age
#include <stdio.h>#include <string.h>typedef struct {char name[20];int age;char gender;} Student;int main() {Student students[10]; // 10个学生的数组int n;printf("请输入学生人数(1-10):");scanf("%d", &n); // 输入for (int i = 0; i < n; i++) {printf("\n第%d个学生:\n", i + 1);printf("姓名:");scanf("%s", students[i].name);printf("年龄:");scanf("%d", &students[i].age);printf("性别(M/F):");scanf(" %c", &students[i].gender); } // 输出printf("\n学生信息列表:\n");printf("================================\n");printf("%-10s %-5s %-5s\n", "姓名", "年龄", "性别");printf("--------------------------------\n");for (int i = 0; i < n; i++) {printf("%-10s %-5d %-5c\n",students[i].name,students[i].age,students[i].gender); }return 0;}
typedef struct {int year;int month;int day;} Date;typedef struct {char name[20];int age;char gender;Date birthday; // 嵌套结构体} Student;int main() {Student s1;strcpy(s1.name, "张三");s1.age = 18;s1.gender = 'M';s1.birthday.year = 2007;s1.birthday.month = 5;s1.birthday.day = 15;printf("%s, %d岁, 生日:%d-%02d-%02d\n",s1.name, s1.age,s1.birthday.year,s1.birthday.month,s1.birthday.day);return 0;}
typedef struct Student {char name[20];int age;struct Student *next; // 指向下一个学生} Student;
这就是链表的基础!
int main() {Student s1 = {"张三", 18, NULL};Student s2 = {"李四", 19, NULL};Student s3 = {"王五", 20, NULL}; // 连接s1.next = &s2;s2.next = &s3; // 遍历Student *p = &s1;while (p != NULL) {printf("%s, %d岁\n", p->name, p->age);p = p->next; }return 0;}
输出:
张三, 18岁李四, 19岁王五, 20岁
结构体好比一个盒,各种数据装一起。
typedef简化声明,点和箭头访问它。
内存对齐要记牢,最大类型作倍数。
自引用指针妙,链表基础就是它。
任务: 定义图书结构体,包含书名、作者、价格、ISBN
点击查看答案
#include <stdio.h>#include <string.h>typedef struct {char title[50];char author[30];double price;char isbn[20];} Book;int main() {Book book1;strcpy(book1.title, "C语言程序设计");strcpy(book1.author, "谭浩强");book1.price = 39.80;strcpy(book1.isbn, "978-7-302-12345-6");printf("书名:%s\n", book1.title);printf("作者:%s\n", book1.author);printf("价格:%.2f元\n", book1.price);printf("ISBN:%s\n", book1.isbn);return 0;}
任务: 预测以下结构体的大小
点击查看答案
#include <stdio.h>typedef struct {char a; // 1字节int b; // 4字节char c; // 1字节} Test1; // 12字节(4的倍数)typedef struct {char a; // 1字节char c; // 1字节int b; // 4字节} Test2; // 8字节(4的倍数)typedef struct {double a; // 8字节int b; // 4字节char c; // 1字节} Test3; // 16字节(8的倍数)int main() {printf("Test1: %d字节\n", sizeof(Test1));printf("Test2: %d字节\n", sizeof(Test2));printf("Test3: %d字节\n", sizeof(Test3));return 0;}
输出:
Test1: 12字节Test2: 8字节Test3: 16字节
任务: 创建3个学生,用指针连接,遍历输出
点击查看答案
#include <stdio.h>#include <string.h>typedef struct Student {char name[20];int age;struct Student *next;} Student;int main() { // 创建学生Student s1, s2, s3;strcpy(s1.name, "张三");s1.age = 18;strcpy(s2.name, "李四");s2.age = 19;strcpy(s3.name, "王五");s3.age = 20; // 连接成链表s1.next = &s2;s2.next = &s3;s3.next = NULL; // 遍历Student *p = &s1;while (p != NULL) {printf("%s, %d岁\n", p->name, p->age);p = p->next; }return 0;}
思考题:
为什么要内存对齐?能否完全避免?如何优化结构体减少内存浪费?结构体和数组有什么区别?如果本文对你有帮助,欢迎:
点赞支持 关注不迷路 评论区分享你对结构体的理解⭐ 收藏慢慢看—-本文为”C++ 大白话”系列第 19 篇
版权声明:本文转载于今日头条,版权归作者所有,如果侵权,请联系本站编辑删除