C语言结构体完全教程
C语言结构体完全教程
一、结构体基本概念
结构体(struct)是C语言中一种重要的自定义数据类型,它允许我们将不同类型的数据组合在一起,形成一个新的数据类型。结构体可以看作是一组相关变量的集合,这些变量可以是不同类型的,它们共同描述一个对象的属性。
为什么需要结构体?
- 当我们需要描述一个具有多个属性的复杂对象时(如学生、员工、图书等),使用单独的变量会非常零散
- 结构体可以将相关数据组织在一起,使代码更加清晰、易于维护
- 结构体是面向对象编程思想在C语言中的体现
二、结构体的定义与声明
2.1 结构体的定义
结构体定义的语法格式:
struct 结构体名 {
数据类型 成员名1;
数据类型 成员名2;
// ... 更多成员
};
示例:定义一个学生结构体
struct Student {
int id; // 学号
char name[20]; // 姓名
int age; // 年龄
float score; // 分数
char gender; // 性别
};
2.2 结构体变量的声明
有三种方式声明结构体变量:
方式1:先定义结构体类型,再声明变量
struct Student {
int id;
char name[20];
int age;
};
struct Student stu1, stu2; // 声明两个Student类型的变量
方式2:定义结构体类型的同时声明变量
struct Student {
int id;
char name[20];
int age;
} stu1, stu2; // 直接在定义后声明变量
方式3:匿名结构体(不指定结构体名)
struct {
int id;
char name[20];
int age;
} stu1, stu2; // 只能在这里声明变量,后续无法再创建该类型变量
三、结构体的初始化
3.1 按顺序初始化
struct Student stu = {1001, "张三", 18, 95.5, 'M'};
3.2 指定成员初始化(C99及以上支持)
struct Student stu = {
.id = 1001,
.name = "张三",
.age = 18,
.score = 95.5,
.gender = 'M'
};
3.3 部分初始化
struct Student stu = {1001, "李四"}; // 未初始化的成员自动为0或NULL
3.4 定义时直接初始化
struct Student {
int id;
char name[20];
int age;
} stu = {1001, "王五", 20};
四、结构体成员的访问方式
4.1 使用点运算符(.)访问普通结构体变量
struct Student stu;
// 赋值
stu.id = 1001;
strcpy(stu.name, "张三");
stu.age = 18;
// 读取
printf("学号:%d\n", stu.id);
printf("姓名:%s\n", stu.name);
printf("年龄:%d\n", stu.age);
4.2 使用箭头运算符(->)访问结构体指针
struct Student stu;
struct Student *p = &stu;
// 通过指针访问成员(两种方式等价)
p->id = 1001; // 方式1:箭头运算符(推荐)
(*p).age = 18; // 方式2:先解引用再用点运算符
printf("学号:%d\n", p->id);
*注意:箭头运算符是C语言的语法糖,p->id 等价于 `(p).id`**
五、结构体数组
结构体数组是指数组中的每个元素都是结构体类型。
5.1 结构体数组的定义和初始化
// 定义一个包含3个学生的数组
struct Student class[3] = {
{1001, "张三", 18, 95.5, 'M'},
{1002, "李四", 19, 88.0, 'F'},
{1003, "王五", 18, 92.5, 'M'}
};
5.2 结构体数组的访问
// 访问第2个学生的姓名
printf("第二个学生:%s\n", class[1].name);
// 遍历所有学生
for (int i = 0; i < 3; i++) {
printf("学号:%d, 姓名:%s, 分数:%.1f\n",
class[i].id, class[i].name, class[i].score);
}
六、结构体指针
6.1 指向结构体变量的指针
struct Student stu = {1001, "张三", 18, 95.5, 'M'};
struct Student *p = &stu;
// 通过指针访问成员
printf("姓名:%s\n", p->name);
printf("分数:%.1f\n", p->score);
6.2 结构体指针数组
struct Student stu1 = {1001, "张三", 18, 95.5, 'M'};
struct Student stu2 = {1002, "李四", 19, 88.0, 'F'};
struct Student *students[2] = {&stu1, &stu2};
// 访问
printf("%s\n", students[0]->name); // 张三
printf("%s\n", students[1]->name); // 李四
6.3 动态分配结构体(malloc)
// 动态分配一个结构体
struct Student *p = (struct Student *)malloc(sizeof(struct Student));
if (p != NULL) {
p->id = 1001;
strcpy(p->name, "张三");
p->age = 18;
printf("动态创建的学生:%s\n", p->name);
free(p); // 释放内存
}
七、结构体作为函数参数
7.1 值传递(传递结构体副本)
// 函数定义
void printStudent(struct Student s) {
printf("学号:%d, 姓名:%s\n", s.id, s.name);
}
// 函数调用
struct Student stu = {1001, "张三", 18, 95.5, 'M'};
printStudent(stu); // 传递整个结构体(会创建副本)
缺点:结构体较大时,值传递效率低
7.2 地址传递(传递结构体指针,推荐)
// 函数定义
void printStudent(const struct Student *s) {
printf("学号:%d, 姓名:%s\n", s->id, s->name);
}
// 修改结构体成员的函数
void setScore(struct Student *s, float newScore) {
s->score = newScore;
}
// 函数调用
struct Student stu = {1001, "张三", 18, 95.5, 'M'};
printStudent(&stu);
setScore(&stu, 100.0);
优点:只传递地址,效率高,还可以修改原结构体
7.3 返回结构体
// 函数返回结构体
struct Student createStudent(int id, const char *name, int age) {
struct Student s;
s.id = id;
strcpy(s.name, name);
s.age = age;
s.score = 0;
s.gender = 'U';
return s;
}
// 使用
struct Student newStu = createStudent(1005, "赵六", 20);
八、嵌套结构体
结构体的成员也可以是另一个结构体类型。
8.1 嵌套结构体定义
// 日期结构体
struct Date {
int year;
int month;
int day;
};
// 学生结构体(包含日期成员)
struct Student {
int id;
char name[20];
struct Date birthday; // 嵌套结构体
struct Date enrollDate;
};
8.2 嵌套结构体初始化
struct Student stu = {
1001,
"张三",
{2005, 5, 15}, // birthday
{2023, 9, 1} // enrollDate
};
8.3 嵌套结构体访问
// 连续使用点运算符
printf("出生年份:%d\n", stu.birthday.year);
printf("入学月份:%d\n", stu.enrollDate.month);
// 如果是指针
struct Student *p = &stu;
printf("出生日:%d\n", p->birthday.day);
九、typedef用法
typedef 用于为已有的数据类型创建别名,简化结构体的使用。
9.1 基本用法
// 方式1:先定义结构体,再typedef
struct Student {
int id;
char name[20];
int age;
};
typedef struct Student Student; // 创建别名Student
// 现在可以直接用Student声明变量
Student stu1, stu2; // 不需要写struct Student
9.2 定义结构体时同时typedef(推荐写法)
typedef struct {
int id;
char name[20];
int age;
float score;
} Student; // 结构体别名为Student
// 使用
Student stu = {1001, "张三", 18, 95.5};
9.3 typedef结构体指针
typedef struct {
int id;
char name[20];
} Student;
typedef Student* StudentPtr; // 结构体指针别名
// 使用
StudentPtr p = (StudentPtr)malloc(sizeof(Student));
p->id = 1001;
9.4 typedef的好处
- 简化代码:不需要每次都写
struct关键字 - 提高可读性:类型名更清晰
- 便于移植:修改类型定义时只需修改一处
十、代码练习题
练习题1:学生信息管理
题目:
定义一个学生结构体,包含学号、姓名、年龄、分数。编写程序实现:
- 输入3个学生的信息
- 计算并输出平均分
- 找出分数最高的学生并输出
参考代码:
#include <stdio.h>
#include <string.h>
typedef struct {
int id;
char name[20];
int age;
float score;
} Student;
int main() {
Student students[3];
float total = 0;
int maxIndex = 0;
// 输入学生信息
for (int i = 0; i < 3; i++) {
printf("请输入第%d个学生信息:\n", i + 1);
printf("学号:");
scanf("%d", &students[i].id);
printf("姓名:");
scanf("%s", students[i].name);
printf("年龄:");
scanf("%d", &students[i].age);
printf("分数:");
scanf("%f", &students[i].score);
total += students[i].score;
// 记录最高分
if (students[i].score > students[maxIndex].score) {
maxIndex = i;
}
}
// 输出平均分
printf("\n平均分:%.2f\n", total / 3);
// 输出最高分学生
printf("\n分数最高的学生:\n");
printf("学号:%d, 姓名:%s, 分数:%.1f\n",
students[maxIndex].id,
students[maxIndex].name,
students[maxIndex].score);
return 0;
}
练习题2:图书管理系统
题目:
定义图书结构体(书名、作者、价格、出版日期),实现以下功能:
- 使用嵌套结构体表示日期
- 实现函数打印图书信息
- 实现函数比较两本书的价格
参考代码:
#include <stdio.h>
#include <string.h>
// 日期结构体
typedef struct {
int year;
int month;
int day;
} Date;
// 图书结构体
typedef struct {
char title[50];
char author[30];
float price;
Date publishDate;
} Book;
// 打印图书信息
void printBook(const Book *book) {
printf("书名:%s\n", book->title);
printf("作者:%s\n", book->author);
printf("价格:%.2f元\n", book->price);
printf("出版日期:%d年%d月%d日\n",
book->publishDate.year,
book->publishDate.month,
book->publishDate.day);
}
// 比较价格,返回较贵的书
Book* comparePrice(Book *b1, Book *b2) {
return (b1->price > b2->price) ? b1 : b2;
}
int main() {
Book book1 = {
"C语言程序设计",
"谭浩强",
39.80,
{2021, 6, 1}
};
Book book2 = {
"数据结构与算法",
"严蔚敏",
45.00,
{2020, 8, 15}
};
printf("=== 图书1 ===\n");
printBook(&book1);
printf("\n=== 图书2 ===\n");
printBook(&book2);
printf("\n价格较高的书是:%s\n", comparePrice(&book1, &book2)->title);
return 0;
}
练习题3:员工工资计算
题目:
定义员工结构体(工号、姓名、基本工资、奖金、扣款),编写程序:
- 计算实发工资(基本工资+奖金-扣款)
- 使用结构体数组存储多个员工
- 按实发工资降序排序并输出
参考代码:
#include <stdio.h>
#include <string.h>
typedef struct {
int id;
char name[20];
float baseSalary; // 基本工资
float bonus; // 奖金
float deduction; // 扣款
float netSalary; // 实发工资
} Employee;
// 计算实发工资
void calculateNetSalary(Employee *emp) {
emp->netSalary = emp->baseSalary + emp->bonus - emp->deduction;
}
// 按实发工资排序(冒泡排序)
void sortBySalary(Employee emps[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (emps[j].netSalary < emps[j + 1].netSalary) {
Employee temp = emps[j];
emps[j] = emps[j + 1];
emps[j + 1] = temp;
}
}
}
}
// 打印员工工资表
void printSalaryTable(Employee emps[], int n) {
printf("%-6s%-10s%-12s%-10s%-10s%-12s\n",
"工号", "姓名", "基本工资", "奖金", "扣款", "实发工资");
printf("--------------------------------------------------------\n");
for (int i = 0; i < n; i++) {
printf("%-6d%-10s%-12.2f%-10.2f%-10.2f%-12.2f\n",
emps[i].id,
emps[i].name,
emps[i].baseSalary,
emps[i].bonus,
emps[i].deduction,
emps[i].netSalary);
}
}
int main() {
Employee emps[] = {
{1001, "张三", 8000, 2000, 500},
{1002, "李四", 6500, 1500, 300},
{1003, "王五", 9000, 3000, 800},
{1004, "赵六", 7500, 1800, 400}
};
int n = sizeof(emps) / sizeof(emps[0]);
// 计算每个人的实发工资
for (int i = 0; i < n; i++) {
calculateNetSalary(&emps[i]);
}
printf("=== 排序前 ===\n");
printSalaryTable(emps, n);
// 排序
sortBySalary(emps, n);
printf("\n=== 按实发工资降序排序后 ===\n");
printSalaryTable(emps, n);
return 0;
}
总结
结构体是C语言中非常重要的数据类型,掌握它需要理解:
- 结构体的本质:将不同类型数据组织在一起
- 两种访问方式:
.用于普通变量,->用于指针 - 函数传参:优先使用指针传递,效率更高
- 嵌套结构体:成员可以是另一个结构体
- typedef:简化类型名,提高代码可读性
通过多做练习,你就能熟练掌握结构体的使用,为后续学习数据结构和面向对象编程打下坚实基础!
扫描二维码,在手机上阅读
版权所有:从0至1
文章标题:C语言结构体完全教程
文章链接:https://www.from0to1.cn/c-language/c-yu-yan-jie-gou-ben-wan-quan-jiao-cheng.html
本站文章均为原创,未经授权请勿用于任何商业用途
文章标题:C语言结构体完全教程
文章链接:https://www.from0to1.cn/c-language/c-yu-yan-jie-gou-ben-wan-quan-jiao-cheng.html
本站文章均为原创,未经授权请勿用于任何商业用途
文章目录
打赏
如果觉得文章对您有用,请随意打赏。
您的支持是我们继续创作的动力!
微信扫一扫
支付宝扫一扫
收藏