编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • C语言入门精通

    • 入门教程

      • README
      • 基础语法
      • 数据类型
      • 运算符
      • 循环和选择
      • 输入输出
      • 函数
      • 指针
      • 数组和容器
      • 类和内存
      • 流与文件
      • 结构体
        • 11.1 结构体介绍
        • 11.2 结构体定义
          • 11.2.1 结构体语法
          • 11.2.2 结构体示例
          • 11.2.3 案例分析
        • 11.3 结构体使用
          • 11.3.1 结构体声明
          • 11.3.2 结构体访问
          • 11.3.3 结构体初始化
          • 11.3.5 结构体大小
          • 11.3.6 结构体嵌套
          • 11.3.7 综合案例与思考
        • 11.5 结构体指针
        • 11.6 函数与结构体
          • 11.6.1 结构体作为函数参数
          • 11.6.2 结构体指针作为函数参数
        • 11.7 结构体和函数
        • 11.8 结构体对齐
        • 11.9 结构体typedef
        • 11.10 注意事项
          • 11.10.1 综合案例与思考
      • 线程和锁
      • 预处理器
      • 高级数据
    • 综合案例

    • 专栏博客

    • 标准集库

  • Cpp入门到精通

  • Java入门精通

  • Go入门到精通

  • JavaScript入门

  • CodeX
  • C语言入门精通
  • 入门教程
杨充
2025-07-21
目录

结构体

# 11.结构体

# 目录介绍

  • 11.1 结构体介绍
  • 11.2 结构体定义
    • 11.2.1 结构体语法
    • 11.2.2 结构体示例
    • 11.2.3 案例分析
  • 11.3 结构体使用
    • 11.3.1 结构体声明
    • 11.3.2 结构体访问
    • 11.3.3 结构体初始化
    • 11.3.5 结构体大小
    • 11.3.6 结构体嵌套
    • 11.3.7 综合案例与思考
  • 11.4 结构体数组
  • 11.5 结构体指针
  • 11.6 函数与结构体
    • 11.6.1 结构体作为函数参数
    • 11.6.2 结构体指针作为函数参数
  • 11.7 结构体和函数
  • 11.8 结构体对齐
  • 11.9 结构体typedef
  • 11.10 注意事项
    • 11.10.1 综合案例与思考

# 11.1 结构体介绍

在 C 语言中,结构体(Structure) 是一种用户定义的数据类型,用于将不同类型的数据组合在一起。

结构体可以包含多个成员变量,每个成员变量可以是不同的数据类型。结构体是 C 语言中实现复杂数据组织的重要工具。

# 11.2 结构体定义

# 11.2.1 结构体语法

使用 struct 关键字定义结构体。

struct 结构体名 {
    数据类型 成员1;
    数据类型 成员2;
    // ...
};
1
2
3
4
5

# 11.2.2 结构体示例

struct Student {
    int id;
    char name[50];
    float score;
};
1
2
3
4
5

# 11.2.3 案例分析

下面这三种有什么区别?

struct tagStudentA {
    char name[20];
};

struct tagStudentB {
    char name[20];
} StudentB;

typedef struct tagStudentC {
    char name[20];
} StudentC;
1
2
3
4
5
6
7
8
9
10
11

# 11.3 结构体使用

# 11.3.1 结构体声明

定义结构体后,可以声明结构体变量。

语法

struct 结构体名 变量名;
1

示例

struct Student stu;
1

# 11.3.2 结构体访问

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

语法

结构体变量名.成员名;
1

示例

stu.id = 1;
strcpy(stu.name, "Alice");
stu.score = 95.5;
1
2
3

# 11.3.3 结构体初始化

可以在声明结构体变量时初始化。

语法

struct 结构体名 变量名 = {值1, 值2, ...};
1

示例

struct Student stu2 = {2, "Bob", 88.5};
1

# 11.3.5 结构体大小

使用 sizeof 运算符可以获取结构体的大小。

示例

struct Student {
    int id;
    char name[50];
    float score;
};

printf("Size of Student: %lu bytes\n", sizeof(struct Student));
1
2
3
4
5
6
7

输出:

Size of Student: 60 bytes
1

# 11.3.6 结构体嵌套

结构体可以包含其他结构体作为成员。

示例

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

struct Employee {
    int id;
    char name[50];
    struct Date hireDate;
};

struct Employee emp1 = {1, "Alice", {2020, 1, 1}};
1
2
3
4
5
6
7
8
9
10
11
12
13

# 11.3.7 综合案例与思考

综合案例:结构体的综合运用——学生管理系统

#include <stdio.h>
#include <string.h>

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

typedef struct {
    int id;
    char name[50];
    float scores[3];   // 语文、数学、英语
    Date birthday;
} Student;

// 计算平均分
float calc_avg(Student *s) {
    float sum = 0;
    for (int i = 0; i < 3; i++) sum += s->scores[i];
    return sum / 3;
}

// 打印学生信息
void print_student(Student *s) {
    printf("ID:%d 姓名:%-6s 生日:%d-%02d-%02d "
           "成绩:[%.0f,%.0f,%.0f] 平均:%.1f\n",
           s->id, s->name,
           s->birthday.year, s->birthday.month, s->birthday.day,
           s->scores[0], s->scores[1], s->scores[2],
           calc_avg(s));
}

// 按平均分排序(冒泡)
void sort_by_avg(Student arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - 1 - i; j++) {
            if (calc_avg(&arr[j]) < calc_avg(&arr[j+1])) {
                Student temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
}

int main() {
    Student students[] = {
        {1, "张三", {85, 92, 78}, {2000, 3, 15}},
        {2, "李四", {95, 88, 91}, {2001, 7, 20}},
        {3, "王五", {72, 96, 85}, {2000, 11, 5}},
        {4, "赵六", {90, 85, 93}, {2001, 1, 30}},
    };
    int n = sizeof(students) / sizeof(students[0]);
    
    printf("=== 学生信息(原始) ===\n");
    for (int i = 0; i < n; i++) print_student(&students[i]);
    
    sort_by_avg(students, n);
    
    printf("\n=== 按平均分排序(降序) ===\n");
    for (int i = 0; i < n; i++) print_student(&students[i]);
    
    // 结构体大小分析
    printf("\n=== 结构体大小 ===\n");
    printf("Date大小: %zu字节\n", sizeof(Date));
    printf("Student大小: %zu字节\n", sizeof(Student));
    printf("注意: 实际大小可能因内存对齐而大于成员大小之和\n");
    
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

原理说明:结构体是C语言中组织复杂数据的核心工具。结构体变量可以直接赋值(编译器生成逐字节拷贝代码),因此 Student temp = arr[j] 是合法的。嵌套结构体使数据组织更加层次化。结构体数组结合函数操作是C语言中实现"数据+操作"的基本模式,虽然没有类的语法糖,但功能上是等价的。注意结构体的大小可能因内存对齐而大于所有成员大小之和。

思考题:

  1. 结构体赋值 s2 = s1 是浅拷贝还是深拷贝?如果成员中有指针会怎样?
  2. 结构体能否用 == 比较?为什么?如何正确比较两个结构体是否相等?
  3. 结构体中成员的声明顺序会影响结构体大小吗?如何通过调整顺序来减少内存占用?

可以定义结构体数组来存储多个结构体变量。

示例

struct Student stuArray[3] = {
    {1, "Alice", 95.5},
    {2, "Bob", 88.5},
    {3, "Charlie", 92.0}
};
1
2
3
4
5

# 11.5 结构体指针

可以使用指针访问结构体成员。

语法

struct 结构体名 *指针名;
指针名->成员名; // 通过指针访问成员
1
2

示例

struct Student *pStu = &stu1;
pStu->id = 3;
strcpy(pStu->name, "David");
pStu->score = 90.0;
1
2
3
4

# 11.6 函数与结构体

# 11.6.1 结构体作为函数参数

struct Point {
    int x;
    int y;
};

void printPoint(struct Point p) {
    printf("(%d, %d)\n", p.x, p.y);
}
1
2
3
4
5
6
7
8

# 11.6.2 结构体指针作为函数参数

void movePoint(struct Point *p, int dx, int dy) {
    p->x += dx;
    p->y += dy;
}
1
2
3
4

# 11.7 结构体和函数

结构体可以作为函数的参数或返回值。

示例

#include <stdio.h>

struct Point {
    int x;
    int y;
};

struct Point addPoints(struct Point p1, struct Point p2) {
    struct Point result;
    result.x = p1.x + p2.x;
    result.y = p1.y + p2.y;
    return result;
}

int main() {
    struct Point p1 = {1, 2};
    struct Point p2 = {3, 4};
    struct Point p3 = addPoints(p1, p2);

    printf("Result: (%d, %d)\n", p3.x, p3.y);
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

输出:

Result: (4, 6)
1

# 11.8 结构体对齐

结构体的成员在内存中可能对齐,以提高访问效率。可以使用 #pragma pack 控制对齐方式。

示例

#pragma pack(1) // 1 字节对齐
struct Packed {
    char a;
    int b;
};
#pragma pack() // 恢复默认对齐

printf("Size of Packed: %lu bytes\n", sizeof(struct Packed));
1
2
3
4
5
6
7
8

输出:

Size of Packed: 5 bytes
1

# 11.9 结构体typedef

可以使用 typedef 为结构体定义别名,简化代码。

语法

typedef struct {
    数据类型 成员1;
    数据类型 成员2;
    // ...
} 别名;
1
2
3
4
5

示例

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

Student stu1 = {1, "Alice", 95.5};
1
2
3
4
5
6
7

# 11.10 注意事项

  1. 成员访问:

    • 使用 . 访问结构体变量成员。
    • 使用 -> 访问结构体指针成员。
  2. 初始化:

    • 结构体变量可以在声明时初始化。
    • 未初始化的结构体变量成员值是未定义的。
  3. 内存对齐:

    • 结构体的大小可能大于成员大小的总和,因为存在内存对齐。
  4. 结构体赋值:

    • 结构体变量可以直接赋值给另一个结构体变量。
    • 示例:
      struct Student stu1 = {1, "Alice", 95.5};
      struct Student stu2 = stu1;
      
      1
      2

# 11.10.1 综合案例与思考

综合案例:结构体实现链表

#include <stdio.h>
#include <stdlib.h>

// 链表节点
typedef struct Node {
    int data;
    struct Node *next;
} Node;

// 创建新节点
Node *node_new(int data) {
    Node *n = (Node *)malloc(sizeof(Node));
    if (n) { n->data = data; n->next = NULL; }
    return n;
}

// 头插法
void list_push_front(Node **head, int data) {
    Node *n = node_new(data);
    n->next = *head;
    *head = n;
}

// 尾插法
void list_push_back(Node **head, int data) {
    Node *n = node_new(data);
    if (*head == NULL) { *head = n; return; }
    Node *cur = *head;
    while (cur->next) cur = cur->next;
    cur->next = n;
}

// 打印链表
void list_print(Node *head) {
    printf("链表: ");
    while (head) {
        printf("%d -> ", head->data);
        head = head->next;
    }
    printf("NULL\n");
}

// 链表长度
int list_length(Node *head) {
    int count = 0;
    while (head) { count++; head = head->next; }
    return count;
}

// 释放链表
void list_free(Node **head) {
    while (*head) {
        Node *temp = *head;
        *head = (*head)->next;
        free(temp);
    }
}

int main() {
    Node *list = NULL;
    
    // 尾插法构建链表
    for (int i = 1; i <= 5; i++) {
        list_push_back(&list, i * 10);
    }
    list_print(list);
    printf("长度: %d\n", list_length(list));
    
    // 头插法添加元素
    list_push_front(&list, 5);
    printf("\n头插5后:\n");
    list_print(list);
    
    // 释放
    list_free(&list);
    printf("\n释放后 head = %p\n", (void *)list);
    
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

原理说明:链表是结构体最经典的应用之一——结构体中包含指向自身类型的指针,形成自引用结构。链表的优势是插入/删除为O(1)(已知位置时),缺点是不支持随机访问、每个节点有额外的指针开销。头插法和尾插法都需要传递 Node **head(二级指针),因为可能需要修改头指针本身。链表是栈、队列、哈希表等高级数据结构的基础。注意:分配的每个节点都必须最终被 free,否则会导致内存泄漏。

思考题:

  1. 链表操作中为什么要传递 Node **head 而不是 Node *head?如果传 Node *head 会怎样?
  2. 如何实现链表的反转?要求时间复杂度O(n)、空间复杂度O(1)。
  3. 单向链表和双向链表各有什么优缺点?在什么场景下选择哪种?
上次更新: 2026/06/10, 11:13:41
流与文件
线程和锁

← 流与文件 线程和锁→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式