结构体的定义、初始化、引用,到结构体指针和数组等方面
C 语言结构体基础核心知识点总结
一、结构体的基本概念与定义
结构体是 C 语言中用于组合不同数据类型的自定义类型,能够将多个相关联的变量封装为一个整体,以表示现实世界中的复杂对象(如学生、商品等)。
1. 结构体的定义语法
结构体的定义由关键字struct、结构体标签、成员列表组成,语法如下:
struct 结构体标签 { 数据类型 成员1; // 结构体内部的变量,可为任意数据类型 数据类型 成员2; // ... 更多成员 }; |
- 结构体标签:用于区分不同结构体的标识符(如student、data_node)。
- 成员:结构体内部的变量,可包含基本类型(int、float 等)、数组、指针,甚至其他结构体。
2. 常见定义方式
(1)普通定义(带标签)
// 定义学生结构体 struct student { char name[128]; // 姓名(字符串) int id; // 学号(整型) float score; // 成绩(浮点型) }; |
- 特点:定义变量时需带struct关键字(如struct student stu;)。
(2)使用typedef取别名
通过typedef为结构体定义别名,简化变量声明:
// 为结构体取别名stu_t(普通变量)和stu_p(指针变量) typedef struct { char name[128]; int id; float score; } stu_t, *stu_p; |
- 特点:定义变量时可直接使用别名(如stu_t stu;或stu_p p;),无需重复struct。
(3)带标签 +typedef(二合一)
兼顾标签和别名的灵活性,便于结构体内部引用自身(如链表节点):
typedef struct data_node { int num; struct data_node *next; // 引用自身结构体指针 } node_t, *node_p; |
二、结构体的初始化
结构体的初始化需为其成员赋值,支持两种方式:普通初始化(按顺序)和指定成员初始化(按名称)。
1. 普通初始化(按顺序)
按成员在结构体中的定义顺序依次赋值,适用于成员较少且顺序固定的场景:
struct student stu1 = {"张三", 1001, 90.5}; // 依次为name、id、score赋值 |
- 局限性:
- 必须按成员顺序赋值,遗漏或顺序错误会导致初始化错误。
- 若结构体新增成员,初始化语句需同步修改,否则可能报错。
2. 指定成员初始化(推荐)
通过成员名显式赋值,不依赖顺序,支持部分初始化,便于结构体升级迭代:
struct student stu2 = { .id = 1002, // 学号 .name = "李四", // 姓名(顺序可任意调整) .score = 88.0 // 成绩 }; |
- 优势:
- 成员初始化顺序可任意调整。
- 可只初始化部分成员(未初始化成员默认值为 0)。
- 结构体新增成员后,原有初始化语句仍可正常使用。
三、结构体成员的引用
结构体成员需通过特定符号访问,根据结构体变量类型(普通变量或指针)使用不同的引用符。
1. 普通结构体变量
使用点运算符(.) 访问成员:
struct student stu; stu.id = 1003; // 为学号赋值 strcpy(stu.name, "王五"); // 为姓名赋值(字符串需用strcpy) stu.score = 95.5; // 为成绩赋值 |
- 注意:字符串成员(如name)不能直接用=赋值(数组名不可修改),需用strcpy函数复制。
2. 结构体指针变量
使用箭头运算符(->) 访问成员(指针指向结构体变量或堆内存):
struct student *p = &stu; // 指针指向栈区结构体变量 p->id = 1004; // 等价于 (*p).id = 1004 strcpy(p->name, "赵六"); // 等价于 strcpy((*p).name, "赵六") |
四、结构体数组与结构体指针
结构体支持数组和指针两种扩展形式,用于批量管理结构体对象或动态分配内存。
1. 结构体数组
由多个相同结构体类型的元素组成,适用于存储多个同类型对象(如多个学生):
// 定义包含5个学生的数组 struct student stu_arr[5] = {0}; // 初始化为0 // 为第一个元素赋值 stu_arr[0].id = 1005; strcpy(stu_arr[0].name, "孙七"); stu_arr[0].score = 92.0; |
- 访问方式:通过数组下标 + 成员引用符(如stu_arr[i].name)。
2. 结构体指针
指向结构体变量或动态分配的结构体内存,灵活管理结构体对象的生命周期:
(1)指向栈区结构体
struct student stu; struct student *p = &stu; // 指针指向栈区变量 p->id = 1006; |
(2)指向堆区结构体(动态内存)
通过malloc申请堆内存,需手动释放(避免内存泄漏):
// 函数:创建堆区结构体并返回指针 struct student* create_stu() { struct student *p = malloc(sizeof(struct student)); // 申请内存 if (p != NULL) { p->id = 1007; strcpy(p->name, "周八"); p->score = 85.5; } return p; } // 使用堆区结构体 struct student *p = create_stu(); printf("学号:%d,姓名:%s\n", p->id, p->name); free(p); // 释放堆内存,避免泄漏 p = NULL; // 避免野指针 |
五、核心注意事项
- 结构体大小与内存对齐:
结构体总大小并非成员大小的简单相加,编译器会自动进行内存对齐(按最大成员对齐数),可通过#pragma pack调整对齐方式(进阶内容)。
- 字符串成员赋值:
结构体中的字符数组(如name[128])不能直接用=赋值,必须使用strcpy、strncpy等函数复制字符串。
- 堆区结构体管理:
动态分配的结构体内存(malloc)必须用free释放,且释放后需将指针置为NULL,避免野指针访问。
- 结构体作为函数参数:
结构体传参时默认按值传递(复制整个结构体),效率较低,推荐传递结构体指针(仅复制地址)。
总结
结构体是 C 语言实现数据封装的核心机制,通过组合不同类型的成员描述复杂对象。掌握结构体的定义(带标签 / 别名)、初始化(指定成员优先)、成员引用(. 与 ->)及扩展形式(数组 / 指针),能有效处理现实世界中的复杂数据。实际开发中,需注意内存管理(尤其是堆区结构体)和字符串成员的正确赋值,确保代码的安全性和可维护性。