2-3 结构体

2-3 结构体 --- 把不同类型打包在一起

⏱ 本节预计用时:25分钟

你前面学了数组------一次装很多个同类型 的数据。比如 int scores[10],10个格子装的都是 int,很整齐。

但现实中的"一组数据",往往不是同类型的。

井字棋的"游戏状态"包含棋盘(char数组)、当前玩家(int)、胜负状态(int)。学生管理系统里,一个学生有姓名(char数组)、学号(int)、成绩(int数组)、平均分(float)。

这些数据类型各不相同,但它们逻辑上是一个整体。 如果把它们拆成零散的变量到处放,代码一长你就找不到谁是谁了。

这一节,教你用「结构体」把它们打包成一个整体。

1 为什么需要结构体

你有一堆零散的纸:一张写着"张三",一张写着"1001",一张写着"85分"。你把这堆纸往桌上一放------一阵风吹来,"张三"和"85分"就分开了。你不知道哪张纸和哪张纸是一组的。

结构体,就是一个档案袋。 它把属于同一个东西的纸装在一起:

  • 这个袋子上写着"学生张三"
  • 打开袋子,学号那张纸在里面,成绩那张纸也在里面
  • 不会丢,不会混

来对比一下。

不用结构体------三样东西散落各处:

c 复制代码
char name[20] = "张三";
int id = 1001;
int score = 85;

// 后来代码里有三个学生...
char name2[20] = "李四";
int id2 = 1002;
int score2 = 92;

char name3[20] = "王五";
int id3 = 1003;
int score3 = 78;

看出来问题了吗?每多一个学生,就要多三个变量。 而且 name、id、score 之间的联系全靠你的脑子记------"这个 id2 是对应 name2 的,别搞混了"。

再来看看井字棋。没有结构体时,游戏状态是三个孤零零的变量:

c 复制代码
char board[9];       // 棋盘在哪?
int currentPlayer;   // 该谁下了?
int gameStatus;      // 游戏结束了吗?

把它们当作参数传给函数的时候,你需要写三个参数;哪天想加一个新属性(比如"移动次数"),所有函数签名都得改------漏改一个就是bug。

用结构体------一个袋子全装好:

c 复制代码
struct Game {
    char board[9];		// 9个格子的棋盘
    int currentPlayer;	// 当前玩家:1或2
    int gameStatus;		// 0=进行中 1=玩家1胜 2=玩家2胜 3=平局
};

struct Game game;  // 一个变量 = 整个游戏状态

棋盘、当前玩家、游戏状态全在里面,想取什么就从袋子里拿,这时的struct Game就像int、double一样,也是一种数据类型!还记得之前讲数据类型时的格子比喻吗?这是你人为创造的,由很多个小格子组成的新的大格子!

2 结构体变量的声明和初始化

2.1 定义结构体变量

定义一个结构体,就像设计一个档案袋的规格:

c 复制代码
struct 结构体名 {
    数据类型 成员1;
    数据类型 成员2;
    // ... 更多成员
};  // 注意:这里有分号

注意 :定义结构体只是画图纸,不是造东西。写在 struct { ... }; 里的代码不会被执行,它只是在告诉编译器:"以后可能有这样一种袋子,里面装这些东西。"

图纸画好了,我们来造个实物:学生结构体。

c 复制代码
struct Student {
    int id;				// 学号
    int scores[3];		// 三科成绩
    double average;		// 平均分
};

2.2 声明结构体变量

声明结构体变量和声明普通变量一样,只是类型名前面要加 struct:

c 复制代码
struct Student stu;      // 造一个Student类型的变量,叫stu,这个struct必须要写,不然会报错。是不是觉得写struct有点啰嗦?下一节会教你怎么用typedef把它省掉。

读作:"struct Student 类型的变量 stu"。

2.3 三种初始化方式

方式一:先声明,后逐个赋值

c 复制代码
struct Student s1;
s1.id = 1001; // .的意思是"的",学生s1的id是1001。
s1.scores[0] = 85; // s1的第一科成绩是85。

方式二:定义时用花括号按顺序初始化

c 复制代码
struct Student s1 = {1001, {85, 90, 78}, 84.33f};

顺序必须和定义结构体时成员出现的顺序一模一样 。第一个是 name,第二个是 id,第三个是 scores(数组用内层花括号),第四个是 average。

方式三:指定成员名初始化(推荐,不会搞错顺序)

c 复制代码
struct Student s1 = {
    .id = 1001,
    .scores = {85, 90, 78},
    .average = 84.33f
};

完整示例:

c 复制代码
#include <stdio.h>

// 1. 定义学生结构体(图纸)
struct Student {
    int id;             // 学号
    int scores[3];      // 三科成绩
    double average;     // 平均分
};

// 封装打印学生信息函数,复用代码
void printStudent(struct Student s) {
    printf("===== 学生信息 =====\n");
    printf("学号:%d\n", s.id);
    printf("三科成绩:%d %d %d\n", s.scores[0], s.scores[1], s.scores[2]);
    printf("平均分:%.2lf\n\n", s.average);
}

int main() {
    // ==================== 方式1:先声明变量,后逐个赋值 ====================
    struct Student s1;
    s1.id = 1001;
    s1.scores[0] = 85;
    s1.scores[1] = 90;
    s1.scores[2] = 78;
    s1.average = 84.33;
    printf("【方式1:逐个赋值初始化】\n");
    printStudent(s1);

    // ==================== 方式2:定义时按顺序整体初始化 ====================
    struct Student s2 = {1002, {92, 88, 95}, 91.67};
    printf("【方式2:顺序花括号初始化】\n");
    printStudent(s2);

    // ==================== 方式3:指定成员名初始化(推荐,顺序无关) ====================
    struct Student s3 = {
        .average = 76.00,
        .id = 1003,
        .scores = {70, 75, 83}
    };
    printf("【方式3:指定成员初始化】\n");
    printStudent(s3);

    return 0;
}

运行结果:

3 结构体数组

一个学生用一个 struct Student。那全班50个学生呢?

用struct Student的数组!

c 复制代码
struct Student class[50];  // 50个Student的数组

如图示,每个 class[i] 本身都是一个完整的 struct Student。

c 复制代码
class[0] → { id:1001, scores:{85,90,78}, average:84.33 }
class[1] → { id:1002, scores:{92,88,95}, average:91.67 }
class[2] → { id:1003, scores:{78,82,80}, average:80.00 }
...
class[49] → { ... }

用循环遍历:

c 复制代码
struct Student class[3] = {
    { 1001, {85, 90, 78}, 84.33f},
    { 1002, {92, 88, 95}, 91.67f},
    { 1003, {78, 82, 80}, 80.00f}
};

for (int i = 0; i < 3; i++) {
    printf("  学生%d:\n", i + 1);
    printf("  学号:%d\n", class[i].id);
    printf("  成绩:%d, %d, %d\n",
           class[i].scores[0],
           class[i].scores[1],
           class[i].scores[2]);
    printf("  平均分:%.2f\n\n", class[i].average);
}

注意 class[i].id 的读法:class 的第 i 个学生,取他的 id 。. 和 [i] 放在一起不怕,从左往右读就行。

4 总结

  1. struct 是把不同类型打包在一起的工具 --- 井字棋的 Game 包了棋盘+玩家+状态,学生管理的 Student 包了姓名+学号+成绩+平均分
  2. 用 . 访问成员 --- game.board[0] 表示"game 的 board 的第0个元素",stu.name 表示"stu 的 name"
  3. 定义完 struct 就可以像普通类型一样用 --- struct Student s; 声明变量,struct Student class[50]; 声明数组,和你用 int、char 一模一样,只是多写一个 struct 关键字
  4. 结构体数组 = 一次管理多个复杂数据 --- struct Student class[50] 一个变量管50个学生,每个学生又是一个完整的结构体

5 练习题

坚持就是胜利!

题目1:定义学生结构体(学号、成绩),定义结构体数组存储3个学生。键盘输入3个学生信息,最后遍历全部输出。

c 复制代码
#include <stdio.h>

struct Student {
    int id;
    int score;
};

int main() {
    struct Student stu[3];

    // 输入
    for(int i = 0; i < 3; i++) {
        scanf("%d%d", &stu[i].id, &stu[i].score);
    }

    // 输出
    for(int i = 0; i < 3; i++) {
        printf("学号:%d,成绩:%d\n", stu[i].id, stu[i].score);
    }

    return 0;
}

题目2:定义生日结构体:年、月、日。加到刚才的学生结构体里:学号、生日、分数,初始化一名学生信息并完整打印。

c 复制代码
#include <stdio.h>

// 嵌套结构体练习
struct Birth {
    int year;
    int month;
    int day;
};

struct Student {
    int id;
    struct Birth birth;
    float score;
};

int main() {
    struct Student s = {
        .id = 202601,
        .birth = {2005, 9, 10},
        .score = 88.0
    };

    printf("学号:%d\n", s.id);
    printf("生日:%d年%d月%d日\n", s.birth.year, s.birth.month, s.birth.day);
    printf("成绩:%.1f\n", s.score);

    return 0;
}
相关推荐
从小白到软工4 小时前
2-2 函数
c
从小白到软工7 小时前
2-1 二维数组
c
书生执笔画浮沉9 小时前
Linux桌面端应用向鸿蒙PC迁移适配纪实
c++·qt·c·信创·海思·libc·ohos
带土112 小时前
15. sscanf 函数详解
c
C++ 老炮儿的技术栈1 天前
sizeof操作符
c语言·c++·人工智能·mfc·c
多弗朗皮卡丘3 天前
数据结构番外:环形队列
数据结构·c
91刘仁德4 天前
传输层协议深度拆解:端口寻址、UDP 报文与 TCP 可靠性机制全解析
linux·网络·c++·网络协议·tcp/ip·udp·c
C++ 老炮儿的技术栈4 天前
不一样的数值交换
数据结构·c++·人工智能·算法·c·csdn开发云
C++ 老炮儿的技术栈4 天前
main函数之后的调用
c语言·c++·人工智能·qt·mfc·c