一、结构体入门:为什么需要结构体

大家好,我是你们的老朋友。今天咱们聊聊C语言里一个特别实用的东西——结构体。

先问大家一个问题:如果你要描述一个学生,需要记录他的学号、姓名、年龄、成绩。你会怎么做?

最直接的办法,定义几个单独的变量:

int stu_id;
char stu_name[20];
int stu_age;
float stu_score;

嗯,这样写没问题。但假如你有100个学生呢?难道要定义400个变量?

更麻烦的是,这些变量之间其实是有关系的——它们都属于同一个学生。但代码里完全看不出这种关联。你想想看,维护这样的代码得多痛苦。

这就是结构体要解决的问题。

结构体允许你把不同类型的数据打包成一个整体。就像用绳子把几本书捆在一起,方便携带和管理。

我在项目中遇到过这样一个场景:需要处理传感器数据,包括温度、湿度、气压、时间戳。如果不用结构体,代码里到处都是零散的变量,传参时得写一长串。用了结构体之后,一个结构体变量就搞定了,代码瞬间清爽很多。

核心思想:结构体是C语言中实现数据聚合的基本工具。它把逻辑上相关的数据组织在一起,让代码更清晰、更易维护。

二、结构体的定义

结构体的定义,说白了就是告诉编译器:我要创建一种新的数据类型,它长什么样。

语法格式如下:

struct 结构体名 {
    数据类型 成员1;
    数据类型 成员2;
    // ... 更多成员
};

注意最后的分号,很多人会漏掉。我刚开始学的时候也犯过这个错,编译器报错半天找不到原因。

举个例子,定义一个学生结构体:

struct Student {
    int id;           // 学号
    char name[20];    // 姓名
    int age;          // 年龄
    float score;      // 成绩
};

这里我定义了一个叫 struct Student 的新类型。它包含四个成员:id、name、age、score。每个成员可以是不同的数据类型。

小技巧:结构体名通常首字母大写,这样一眼就能看出是自定义类型。这是我个人的编码习惯,推荐你也试试。

结构体可以嵌套定义,也就是结构体里面包含另一个结构体:

struct Date {
    int year;
    int month;
    int day;
};

struct Student {
    int id;
    char name[20];
    struct Date birthday;  // 嵌套结构体
    float score;
};

这样,学生的生日信息就被组织得更清晰了。

三、结构体变量的声明

定义好结构体类型之后,就可以用它来声明变量了。有三种方式:

方式一:先定义类型,再声明变量

struct Student stu1;      // 声明一个Student类型的变量
struct Student stu2, stu3; // 一次声明多个变量

这是最常用的方式。类型定义和变量声明分开,代码结构清晰。

方式二:定义类型的同时声明变量

struct Student {
    int id;
    char name[20];
    int age;
    float score;
} stu1, stu2;

这种方式适合临时用一下,不需要重复使用这个类型。但说实话,我很少这么写,因为后面想再用这个类型还得重新定义。

方式三:使用typedef简化

这是我最喜欢的方式。用 typedef 给结构体起个新名字,省得每次都要写 struct 关键字:

typedef struct {
    int id;
    char name[20];
    int age;
    float score;
} Student;

Student stu1;  // 直接使用,不用写struct
Student stu2;

你看,代码简洁多了。很多嵌入式项目的代码里都这么用。

注意:使用typedef时,结构体名可以省略。但如果你需要自引用(比如链表节点),就必须保留结构体名。

四、结构体变量的初始化

声明了变量,总得给它赋初值吧。结构体的初始化也有几种玩法。

方式一:按成员顺序初始化

struct Student stu1 = {1001, "张三", 20, 88.5};

大括号里的值按成员定义的顺序依次赋值。第一个给id,第二个给name,以此类推。

这种方式简单直接,但有个坑:如果结构体成员顺序变了,初始化代码也得跟着改。我曾经在项目里吃过这个亏,结构体加了一个成员,忘了更新初始化代码,结果数据全乱了。

方式二:指定成员初始化(C99支持)

struct Student stu2 = {
    .id = 1002,
    .name = "李四",
    .score = 92.0,
    .age = 21
};

这种方式更安全,不用关心成员顺序。而且可读性更好,一眼就能看出每个值赋给了谁。

我个人强烈推荐这种方式。尤其是结构体成员比较多的时候,指定初始化能避免很多低级错误。

方式三:部分初始化

struct Student stu3 = {1003, "王五"};  // 只初始化前两个成员

未初始化的成员会被自动赋值为0(对于指针类型是NULL)。但要注意,部分初始化只支持从头开始,不能跳过前面的成员。

重要提醒:结构体变量如果未初始化,其成员的值是不确定的(垃圾值)。使用前一定要初始化,否则程序行为不可预测。

五、知识体系总览

下面这张图总结了本章的核心内容,帮你理清思路:

结构体入门知识体系 结构体 为什么需要结构体 • 数据聚合,逻辑相关 • 代码清晰,易于维护 • 传参方便,减少冗余 结构体的定义 • struct 关键字 • 成员列表 • 嵌套结构体 变量的声明 • 先定义后声明 • 定义时声明 • typedef 简化 变量的初始化 • 顺序初始化 • 指定成员初始化 • 部分初始化

六、避坑指南

最后,分享几个我踩过的坑:

  • 忘记分号:结构体定义末尾的分号不能省。我曾经因为这个错误查了半小时,最后发现就是少了个分号。
  • 结构体对齐:编译器可能会在成员之间插入填充字节,导致结构体大小不等于成员大小之和。这在网络通信和文件读写时要特别注意。
  • 浅拷贝问题:结构体赋值是浅拷贝。如果成员包含指针,两个结构体变量会指向同一块内存,修改一个会影响另一个。

我的建议:刚开始学结构体,先掌握最基本的定义、声明和初始化。等用熟练了,再慢慢研究对齐、嵌套、位域这些高级特性。一步一步来,别着急。

好了,结构体入门就讲到这里。记住一句话:结构体是C语言组织数据的基石,用好它,你的代码质量会上一个台阶。


公众号:蓝海资料掘金营,微信deep3321