09-C语言结构体

plana_lab 发布于 18 天前 103 次阅读


前面我们学习了数组。

数组可以把多个相同类型的数据放在一起:

int a[5] = {10, 20, 30, 40, 50};

但是现实中的一个对象,通常不只有一种数据。

例如一个学生:

姓名 -> 字符串
年龄 -> int
成绩 -> double

如果使用普通变量:

char name[50] = "Alice";
int age = 18;
double score = 95.5;

当学生越来越多时,就会变得很麻烦。

结构体就是用来解决这个问题的。


一、什么是结构体

结构体(struct)可以把不同类型的数据组合在一起。

例如:

struct Student {
    char name[50];
    int age;
    double score;
};

这里定义了一个 Student 结构体。

可以理解为:

Student
├─ name
├─ age
└─ score

其中:

char name[50];
int age;
double score;

叫做结构体的成员。

注意:

上面的代码只是定义了一种结构体类型。

还没有创建真正的变量。


二、struct 定义

基本格式:

struct 结构体名字 {
    数据类型 成员1;
    数据类型 成员2;
    数据类型 成员3;
};

例如:

struct Student {
    char name[50];
    int age;
    double score;
};

注意最后一定要有:

};

也就是结构体定义后需要一个分号。


三、结构体变量

定义结构体之后,就可以创建变量。

例如:

struct Student s;

这里:

struct Student

是类型。

s

是变量。

完整代码:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    double score;
};

int main() {
    struct Student s;

    return 0;
}

四、成员访问 .

结构体变量可以使用:

.

访问成员。

例如:

struct Student s;

s.age = 18;
s.score = 95.5;

访问方式:

变量.成员

例如:

s.age
s.score

修改结构体成员

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    double score;
};

int main() {
    struct Student s;

    s.age = 18;
    s.score = 95.5;

    printf("age = %d\n", s.age);
    printf("score = %.2f\n", s.score);

    return 0;
}

运行:

age = 18
score = 95.50

五、结构体初始化

结构体可以在创建的时候直接初始化。

例如:

struct Student s = {"Alice", 18, 95.5};

对应关系:

name  -> "Alice"
age   -> 18
score -> 95.5

完整代码:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    double score;
};

int main() {
    struct Student s = {"Alice", 18, 95.5};

    printf("name = %s\n", s.name);
    printf("age = %d\n", s.age);
    printf("score = %.2f\n", s.score);

    return 0;
}

六、指定成员初始化

还可以指定初始化某个成员:

struct Student s = {
    .name = "Alice",
    .age = 18,
    .score = 95.5
};

这样代码更加清楚。

例如:

struct Student s = {
    .age = 18,
    .score = 95.5
};

没有初始化的成员会使用对应类型的零值。


七、结构体数组

既然数组可以保存多个相同类型的变量,那么当然也可以创建:

结构体数组。

例如:

struct Student students[3];

表示:

students[0]
students[1]
students[2]

每一个元素都是:

struct Student

初始化结构体数组

struct Student students[3] = {
    {"Alice", 18, 95.5},
    {"Bob", 19, 88.0},
    {"Charlie", 18, 92.5}
};

遍历:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    double score;
};

int main() {
    struct Student students[3] = {
        {"Alice", 18, 95.5},
        {"Bob", 19, 88.0},
        {"Charlie", 18, 92.5}
    };

    for (int i = 0; i < 3; i++) {
        printf(
            "%s %d %.2f\n",
            students[i].name,
            students[i].age,
            students[i].score
        );
    }

    return 0;
}

这里结合了之前学过的:

数组
+
for
+
结构体

八、结构体指针

结构体当然也可以使用指针。

例如:

struct Student s = {"Alice", 18, 95.5};

struct Student *p = &s;

这里:

s
↓
Student 变量

&s
↓
s 的地址

p
↓
保存 s 的地址

所以:

p

是一个:

struct Student *

类型的指针。


九、通过结构体指针访问成员

如果使用普通结构体变量:

s.age

如果使用结构体指针:

p->age

这里出现了新的运算符:

->

例如:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    double score;
};

int main() {
    struct Student s = {"Alice", 18, 95.5};

    struct Student *p = &s;

    printf("%s\n", p->name);
    printf("%d\n", p->age);
    printf("%.2f\n", p->score);

    return 0;
}

十、. 和 -> 的区别

这是结构体非常重要的知识。

结构体变量

使用:

.

例如:

s.age

结构体指针

使用:

->

例如:

p->age

可以简单记成:

结构体变量
    ↓
   .

结构体指针
    ↓
   ->

实际上:

p->age

等价于:

(*p).age

也就是说:

p->age

可以理解为:

(*p).age

十一、为什么 (*p).age 要加括号

不能写:

*p.age

因为运算符优先级会导致它被理解成:

*(p.age)

而 p 是指针,并没有 age 这个成员。

所以应该写:

(*p).age

而:

p->age

就是专门为结构体指针设计的简写。


十二、结构体作为函数参数

结构体可以传给函数。

例如:

void print_student(struct Student s) {
    printf("%s\n", s.name);
    printf("%d\n", s.age);
    printf("%.2f\n", s.score);
}

完整代码:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    double score;
};

void print_student(struct Student s) {
    printf("name: %s\n", s.name);
    printf("age: %d\n", s.age);
    printf("score: %.2f\n", s.score);
}

int main() {
    struct Student s = {"Alice", 18, 95.5};

    print_student(s);

    return 0;
}

这里和普通变量传参类似:

print_student(s);

函数收到的是结构体的一份副本。


十三、结构体指针作为函数参数

如果结构体比较大,复制整个结构体可能没有必要。

可以直接传地址。

例如:

void print_student(struct Student *p) {
    printf("%s\n", p->name);
    printf("%d\n", p->age);
    printf("%.2f\n", p->score);
}

调用:

print_student(&s);

完整代码:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    double score;
};

void print_student(struct Student *p) {
    printf("name: %s\n", p->name);
    printf("age: %d\n", p->age);
    printf("score: %.2f\n", p->score);
}

int main() {
    struct Student s = {"Alice", 18, 95.5};

    print_student(&s);

    return 0;
}

十四、通过函数修改结构体

这一点非常重要。

如果直接传结构体:

void change(struct Student s)

修改的是副本。

例如:

void change(struct Student s) {
    s.age = 20;
}

调用:

change(s);

原来的:

s.age

不会发生变化。


但是如果传指针:

void change(struct Student *p) {
    p->age = 20;
}

然后:

change(&s);

原来的 s 就会被修改。

完整代码:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    double score;
};

void change_age(struct Student *p) {
    p->age = 20;
}

int main() {
    struct Student s = {"Alice", 18, 95.5};

    printf("修改前: %d\n", s.age);

    change_age(&s);

    printf("修改后: %d\n", s.age);

    return 0;
}

输出:

修改前: 18
修改后: 20

这和你之前学习的:

void swap(int *a, int *b)

本质上是同一种思想:

把地址传进去
↓
函数通过地址
↓
直接修改原来的数据

十五、结构体作为函数返回值

函数也可以返回一个结构体。

例如:

struct Student create_student() {
    struct Student s = {"Alice", 18, 95.5};

    return s;
}

然后:

struct Student s = create_student();

完整代码:

#include <stdio.h>

struct Student {
    char name[50];
    int age;
    double score;
};

struct Student create_student() {
    struct Student s = {"Alice", 18, 95.5};

    return s;
}

int main() {
    struct Student s = create_student();

    printf("%s\n", s.name);
    printf("%d\n", s.age);
    printf("%.2f\n", s.score);

    return 0;
}

十六、typedef

每次写:

struct Student

稍微有一点麻烦。

可以使用 typedef 给结构体类型起一个别名。

例如:

typedef struct {
    char name[50];
    int age;
    double score;
} Student;

以后就可以直接写:

Student s;

而不用:

struct Student s;

typedef 的基本格式

typedef 原来的类型 新名字;

例如:

typedef int Integer;

那么:

Integer a = 10;

实际上就是:

int a = 10;

十七、typedef + struct

推荐掌握这种写法:

typedef struct {
    char name[50];
    int age;
    double score;
} Student;

使用:

Student s;

完整代码:

#include <stdio.h>

typedef struct {
    char name[50];
    int age;
    double score;
} Student;

int main() {
    Student s = {"Alice", 18, 95.5};

    printf("%s\n", s.name);
    printf("%d\n", s.age);
    printf("%.2f\n", s.score);

    return 0;
}

十八、结构体综合示例

把目前学到的内容组合起来:

#include <stdio.h>

typedef struct {
    char name[50];
    int age;
    double score;
} Student;

void print_student(Student *p) {
    printf("姓名: %s\n", p->name);
    printf("年龄: %d\n", p->age);
    printf("成绩: %.2f\n", p->score);
}

void add_score(Student *p, double score) {
    p->score += score;
}

int main() {

    Student students[3] = {
        {"Alice", 18, 95.5},
        {"Bob", 19, 88.0},
        {"Charlie", 18, 92.5}
    };

    add_score(&students[0], 2.0);

    for (int i = 0; i < 3; i++) {
        print_student(&students[i]);
        printf("\n");
    }

    return 0;
}

这里已经同时使用了:

typedef
struct
数组
函数
指针
.
->
for

这就是结构体真正的意义:

把多个有关联的数据组织成一个完整的对象。


十九、结构体在内存中的表现

例如:

struct Student {
    int age;
    double score;
};

创建:

struct Student s;

可以粗略理解成:

s
┌──────────────┐
│ age          │
├──────────────┤
│ score        │
└──────────────┘

而:

&s

就是整个结构体的地址。

如果:

struct Student *p = &s;

那么:

p
 ↓
┌──────────────┐
│ age          │
├──────────────┤
│ score        │
└──────────────┘

因此:

p->age

就是通过指针访问结构体内部的 age。


二十、结构体与数组的区别

数组:

int a[3];

只能保存:

int
int
int

结构体:

struct Student {
    char name[50];
    int age;
    double score;
};

可以保存:

char[]
int
double

所以:

数组
↓
多个相同类型的数据

结构体
↓
多个不同类型、但逻辑相关的数据

两者也可以结合:

Student students[100];

表示:

100 个 Student。


二十一、结构体与指针的关系

目前已经可以把这几个东西联系起来:

结构体
    ↓
Student s

指针
    ↓
Student *p = &s

普通成员访问
    ↓
s.age

指针成员访问
    ↓
p->age

而:

p->age

本质上就是:

(*p).age

二十二、练习

练习 1

定义一个 Person 结构体:

姓名
年龄
身高

然后创建一个变量并输出。


练习 2

定义:

struct Student

包含:

name
age
score

创建一个包含 5 个学生的结构体数组。

使用 for 输出所有学生。


练习 3

编写:

void print_student(Student *p)

输出学生的信息。

要求使用:

->

访问成员。


练习 4

编写:

void add_score(Student *p, double score)

让函数能够修改学生成绩。


练习 5

编写:

Student create_student(...)

让函数创建并返回一个学生结构体。


练习 6

定义一个:

typedef struct

把结构体类型简化成:

Student

然后使用:

Student s;

创建变量。

此作者没有提供个人介绍。
最后更新于 2026-09-17