基本类型只能存一个值,数组只能存一堆同类型。想表达一个学生有姓名、学号、成绩,三个不同类型绑在一起?结构体(struct)就是干这个的:它能把好几个不同类型的成员装进一个大变量。

结构体是什么

C 语言的结构体是一种特别的大变量,里面装着好几个有名字的小变量。它也是后面对象、类思想的源头。结构体的典型用途:

  • 数据的序列化、持久化。
  • 作为单个参数,把一堆相关的值打包传进、传出函数。
  • 搭链表、二叉树这类复杂数据结构。

最经典的入门示例是点,一个实体里装着 x、y 两个坐标:

struct point {
    int x;
    int y;
};

注意结尾有分号,结构体的定义是一整条语句。现在用它:

struct point p;
p.x = 10;
p.y = 5;
draw(p);   /* 一个参数就把两个坐标带过去了 */

对比一下没有结构体的苦日子:

int x = 10;
int y = 5;
draw(x, y);   /* 传两个参数才表达一个点 */

访问结构体成员用点运算符 .

struct point p int x = 10(4 字节) int y = 5(4 字节) 用 p.x、p.y 分别访问这两个成员
p 是一个大变量,里面装着有名字的小变量 x、y,通过点运算符分别访问。

typedef:给类型起个别名

每次写 struct point 太长。typedef 能给它换个短名字:

typedef struct {
    int x;
    int y;
} point;

这样再定义点就不用写 struct 关键字了:

point p;
p.x = 10;
p.y = 5;

结构体还能装指针,这样它就能存字符串,或指向另一个结构体(这才是它最值钱的地方):

C 语言 typedef 结构体示例运行
typedef struct {
    char *brand;    /* 品牌,char 指针即字符串 */
    int model;      /* 车型年份 */
} vehicle;

vehicle mycar;
mycar.brand = "Ford";
mycar.model = 2007;

结构体指针与箭头运算符

结构体变量用 .,结构体指针要用 ->(上一章按引用传参已经见过):

C 语言结构体指针访问示例运行
point p;
point *ptr = &p;

p.x = 1;          /* 直接访问 */
(*ptr).x = 2;     /* 解引用后访问 */
ptr->x = 3;       /* 简写,等价于 (*ptr).x */

记忆法:实际对象用点,对象指针用箭头。

声明、初始化、成员访问

惯例是从声明到访问一口气走完。下面这段把结构体声明、初始化、成员访问、sizeof 全演示一遍:

C 语言结构体示例运行
#include <stdio.h>

struct Student {
    int rollNo;
    float height;
    char firstLetter;
};

int main() {
    struct Student stu1;
    stu1.rollNo = 21;
    stu1.height = 1.75f;
    stu1.firstLetter = 'X';
    printf("学号 %d,身高 %.2f,姓名首字母 %c\n",
           stu1.rollNo, stu1.height, stu1.firstLetter);

    /* 一行初始化:按成员顺序给值 */
    struct Student stu2 = {30, 1.82f, 'M'};
    printf("第二个学生学号 %d\n", stu2.rollNo);

    printf("一个 Student 占 %zu 字节\n", sizeof(struct Student));
    return 0;
}

结构体 vs 数组:一处关键区别

  • 数组:同类型、连续、按下标访问,快,但只能一种类型。
  • 结构体:可以由不同类型拼成,灵活,但有内部对齐(成员之间可能留空隙,下一节讲到 sizeof 时会发现它不一定等于成员直接相加)。

下一篇看它的近亲,共用体 union,它俩最大的分歧就在内存布局。