C 语言 - 结构体(struct)解析
struct 是什么
- struct就是一种“自定义数据类型”,可以把多个不同类型的变量打包在一起,组成一个整体,表示定义一个结构体类型。
举个通俗例子
- 
假设保存一个“学生”的信息,有三个内容: - 姓名(字符串)
- 年龄(整数)
- 成绩(浮点数)
 
- 
如果不用 struct,你得写三个独立变量:char name[20]; // 存放姓名的字符数组,最多能存放 19 个字符 + 1 个终止符 '\0' //在 C 语言中,char name[20] 这个数组最多只能存 19 个字符,最后一个位置必须留给字符串结束符 '\0' int age; // 年龄 float score; //成绩
- 
如果有 300 个学生就要 600 个变量,既不方便传递,也不方便管理。这时候就可以使用 struct 打包成一个“学生类型”: - 
假设 1 个学生 struct Student { //定义了一个结构体类型,叫 struct Student //里面包含 3 个“成员变量” char name[20]; int age; float score; }; //使用方式 struct Student s1 = {"Alice", 18, 95.5}; //现在 s1 就是一个“学生对象”,里面同时有名字、年龄、成绩三样东西。 //访问方式 printf("%s 的年龄是 %d,成绩是 %.1f\n", s1.name, s1.age, s1.score);
- 
假设 300 个学生 #include <stdio.h> #include <stdlib.h> struct Student { char name[20]; int age; float score; }; int main() { int n; printf("请输入学生人数(最多300人):"); scanf("%d", &n); if (n <= 0 || n > 300) { printf("人数无效!\n"); return 1; } // 定义结构体数组(最多300人) // 将定义好的结构体类型 struct Student,作为一种数据类型,用它创建一个有 300 个成员的数组变量 students struct Student students[300]; // 输入学生信息 for (int i = 0; i < n; i++) { printf("请输入第 %d 个学生的 姓名 年龄 成绩:", i + 1); scanf("%s %d %f", students[i].name, &students[i].age, &students[i].score); } // 输出学生信息并计算平均成绩 float sum = 0; printf("\n------ 学生信息列表 ------\n"); for (int i = 0; i < n; i++) { printf("%-10s 年龄: %2d 成绩: %.1f\n", students[i].name, students[i].age, students[i].score); sum += students[i].score; } printf("----------------------------\n"); printf("平均成绩:%.2f\n", sum / n); return 0; }
 
- 
- 
在内存中表示为: 偏移地址 内容 类型 大小 +0 name[20] char[20] 20字节 +20 age int 4字节 +24 score float 4字节 总计 28字节(单个字节最大为4,对齐后可能是28+4=32字节) - 也就是说,一个结构体是一块连续的内存区域,里面按顺序放着它的每个成员。
 
结构体的局限性
不能有成员函数
- 
只能放数据,不能直接放行为(函数),想让结构体“会做事情”,只能用函数指针或者写单独的函数 struct Person { char name[20]; int age; void (*sayHello)(void); // 可以存函数指针,但不是真正的成员函数 };
没有继承机制
- 
结构体不能继承其他结构体,想模拟“继承”,只能把父结构体放在子结构体里作为成员 struct A { int x; }; struct B { struct A parent; // 模拟继承(B的成员继承A的成员) int y; };
没有访问控制(封装)
- 结构体成员全都是公开的,没有 private/protected/public,外部代码可以直接访问任意成员。
不支持多态
- 
结构体不能像 C++ 类一样通过虚函数实现多态(C语言结构体没有“变身能力”,不同结构体不能表现出不同行为),如果想用“多态”,只能自己写函数指针表(vtable)来模拟,非常麻烦 #include <stdio.h> // 定义结构体类型 struct Animal { // 声明一个函数指针,用于保存“发声”函数的地址 void (*speak)(void); }; // 定义不同动物各自的发声函数 void dogSpeak() { printf("汪汪\n"); } void catSpeak() { printf("喵喵\n"); } int main() { // 创建结构体变量,并让函数指针分别指向不同的发声函数 struct Animal dog = { dogSpeak }; struct Animal cat = { catSpeak }; // 模拟面向对象的多态效果 dog.speak(); // 汪汪 cat.speak(); // 喵喵 // 虽然可以实现,但不像 C++/Java 那样天然支持多态 return 0; }
不能直接返回抽象对象
- 
C语言结构体只能返回值或者指针 #include <stdio.h> // 标准输入输出(若要打印调试信息时需要) #include <stdlib.h> // malloc、free 等需要的头文件 #include <string.h> // strcpy/strncpy 等字符串函数需要的头文件 //返回值 // 定义一个结构体类型 Person,包含 name 和 age 两个成员 struct Person { char name[20]; // 存放姓名的字符数组,最多能存放 19 个字符 + 1 个终止符 '\0' int age; // 年龄 }; // 定义一个函数,返回 struct Person 的“值”(按值返回) struct Person createPerson() { struct Person p; // 在栈上创建一个“人”变量 p(临时工) strcpy(p.name, "张三"); // 给 p 的名字字段赋值 p.age = 20; // 给 p 的年龄字段赋值 return p; // 把整个“人”复制一份返回(返回的是副本) } //函数里临时造了一个“张三”,然后复制一份“张三”的资料带出去。 //函数结束后,里面的临时变量会消失,但返回的那份副本还在外面 // 按值返回是“复制一份交出去”; ============================================================= // 返回指针版本 // 定义一个函数:在“堆”上创建一个 Person,并返回它的地址 struct Person* createPersonPtr() { // 在堆上分配一块内存,用来放一个 struct Person struct Person* p = malloc(sizeof(struct Person)); // 判断是否分配成功(非常重要的安全检查) if (p == NULL) { return NULL; // 如果分配失败,就返回空指针,让调用者自己判断 } // 给结构体中的 name 字段赋值 // “->” 是访问指针所指结构体成员的运算符,相当于 (*p).name strcpy(p->name, "李四"); // 给结构体中的 age 字段赋值 p->age = 25; // “->” 的优先级比点高,所以 p->age 相当于 (*p).age // 返回在堆上创建的这个“人”的地址 // 调用者要记得使用完后 free(p),否则会造成内存泄漏 return p; } // 返回指针是“告诉你原件在哪”
- 
它没有类的抽象和接口,所以高级封装和抽象都要靠函数 + 指针手动实现,就像是模拟多态 #include <stdio.h> struct Animal { void (*speak)(void); }; void dogSpeak() { printf("汪汪\n"); } void catSpeak() { printf("喵喵\n"); } int main() { struct Animal dog = { dogSpeak }; struct Animal cat = { catSpeak }; dog.speak(); // 输出 汪汪 cat.speak(); // 输出 喵喵 }
C/C++结构体对比
| 特性 | C语言结构体 | C++ 类 | 
|---|---|---|
| 数据成员 | ✅ | ✅ | 
| 成员函数 | ❌(只能用函数指针模拟) | ✅ | 
| 继承 | ❌ | ✅ | 
| 多态 | ❌(只能手动模拟) | ✅ | 
| 封装 | ❌ | ✅ | 
| 抽象类/接口 | ❌ | ✅ | 
总结
| 用途 | 说明 | 
|---|---|
| 打包数据 | 把多个不同类型的变量组合成一个整体 | 
| 自定义类型 | 可创建新的“复合类型”供多处复用 | 
| 便于传参与管理 | 可以整体传给函数或数组中存储 | 
- struct是 C 语言中用来创建“自定义复合类型”的修饰符,能把多种不同类型的变量打包成一个整体,让代码更清晰、可扩展。
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号