在C语言中,基本数据类型(int、float、char等)只能表示单一的数据。然而,现实世界中的实体往往包含多个属性,比如一个学生有学号、姓名、成绩等。 结构体(struct) 允许我们将不同类型的数据组合成一个新的数据类型,方便组织和操作复杂数据。 而联合体(union) 则是一种特殊的结构,它允许同一块内存存储不同类型的数据,但同一时刻只能存储其中一种。 本章将详细介绍结构体和联合体的定义、使用以及它们的区别。
结构体是一种用户自定义的数据类型,它可以将多个不同类型的数据项组合成一个整体。结构体的成员可以是基本类型、数组、指针,甚至其他结构体。
struct 结构体名 {
数据类型 成员1;
数据类型 成员2;
// ...
}; // 注意分号
例如,定义一个学生结构体:
struct Student {
int id;
char name[50];
float score;
};
#include <stdio.h>
struct Student {
int id;
char name[50];
float score;
};
int main() {
// 方式1:先声明变量,再逐个赋值
struct Student stu1;
stu1.id = 1001;
// stu1.name = "张三"; // 错误!数组不能直接赋值,需要使用strcpy
strcpy(stu1.name, "张三");
stu1.score = 88.5;
// 方式2:声明时初始化(按成员顺序)
struct Student stu2 = {1002, "李四", 92.0};
// 方式3:指定成员初始化(C99)
struct Student stu3 = {.id = 1003, .score = 78.5, .name = "王五"};
return 0;
}
strcpy 函数(需包含 <string.h>)。
使用点运算符 . 访问结构体变量的成员。
printf("学号: %d\n", stu1.id);
printf("姓名: %s\n", stu1.name);
printf("成绩: %.1f\n", stu1.score);
结构体数组可以存储多个相同类型的结构体对象,方便批量处理。
#include <stdio.h>
#include <string.h>
struct Student {
int id;
char name[20];
float score;
};
int main() {
struct Student class[3] = {
{1001, "张三", 85.5},
{1002, "李四", 92.0},
{1003, "王五", 78.5}
};
for (int i = 0; i < 3; i++) {
printf("%d\t%s\t%.1f\n", class[i].id, class[i].name, class[i].score);
}
return 0;
}
结构体指针指向结构体变量的地址,通过箭头运算符 -> 访问成员。
#include <stdio.h>
struct Point {
int x;
int y;
};
int main() {
struct Point p1 = {10, 20};
struct Point *ptr = &p1;
// 使用指针访问成员
printf("x = %d, y = %d\n", ptr->x, ptr->y); // 等价于 (*ptr).x
ptr->x = 30;
printf("修改后 x = %d\n", p1.x);
return 0;
}
结构体的成员可以是另一个结构体,实现更复杂的数据组织。
#include <stdio.h>
struct Date {
int year;
int month;
int day;
};
struct Student {
int id;
char name[50];
struct Date birthday; // 嵌套结构体
float score;
};
int main() {
struct Student stu = {1001, "张三", {2000, 5, 20}, 88.5};
printf("生日: %d年%d月%d日\n", stu.birthday.year, stu.birthday.month, stu.birthday.day);
return 0;
}
结构体可以作为函数参数传递,但通常有两种方式:传值(复制整个结构体)和传指针(传递地址)。传指针效率更高,且可以修改原结构体。
#include <stdio.h>
#include <string.h>
struct Student {
int id;
char name[50];
float score;
};
// 传值(复制结构体)
void printStudent(struct Student s) {
printf("学号: %d, 姓名: %s, 成绩: %.1f\n", s.id, s.name, s.score);
}
// 传指针(可修改原结构体)
void updateScore(struct Student *s, float newScore) {
s->score = newScore; // 修改原结构体的成绩
}
int main() {
struct Student stu = {1001, "张三", 85.0};
printStudent(stu);
updateScore(&stu, 95.0);
printStudent(stu);
return 0;
}
联合体是一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型。所有成员共享同一块内存,同一时刻只能使用其中一个成员。
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
data.i = 10;
printf("data.i = %d\n", data.i);
data.f = 3.14;
printf("data.f = %f\n", data.f);
printf("data.i = %d(被覆盖)\n", data.i); // 此时 i 的值已改变
return 0;
}
联合体常用于节省内存(如嵌入式系统)或实现变体类型(如数据包解析)。
#include <stdio.h>
// 用联合体存储不同类型的数据
union Value {
int intVal;
float floatVal;
char charVal;
};
struct Variant {
int type; // 0:int, 1:float, 2:char
union Value val;
};
void printVariant(struct Variant v) {
switch (v.type) {
case 0: printf("整数: %d\n", v.val.intVal); break;
case 1: printf("浮点数: %f\n", v.val.floatVal); break;
case 2: printf("字符: %c\n", v.val.charVal); break;
}
}
int main() {
struct Variant v1 = {0, {.intVal = 100}};
struct Variant v2 = {1, {.floatVal = 3.14}};
struct Variant v3 = {2, {.charVal = 'A'}};
printVariant(v1);
printVariant(v2);
printVariant(v3);
return 0;
}
| 特性 | 结构体(struct) | 联合体(union) | 32 32内存分配 | 每个成员独立分配内存,总大小≥各成员大小之和 | 所有成员共享同一块内存,大小等于最大成员大小 | 32 32成员访问 | 所有成员可同时使用 | 同一时刻只能使用一个成员,赋值会覆盖其他成员 | 32 32用途 | 组织不同类型的数据,形成记录 | 节省内存,或同一块内存不同解释 | 32
|---|
typedef 用于为已有类型创建别名,提高代码可读性,常用于简化结构体声明。
// 不使用 typedef
struct Student {
int id;
char name[50];
};
struct Student stu1; // 每次都要写 struct
// 使用 typedef
typedef struct {
int id;
char name[50];
} Student; // 现在 Student 就是一个类型名
Student stu2; // 直接使用
// 也可以分开写
typedef struct Point {
int x;
int y;
} Point;
Point p1; // 可以直接用
struct Point p2; // 原来的也可以
#include <stdio.h>
#include <string.h>
#define MAX_STUDENTS 100
typedef struct {
int id;
char name[50];
float score;
} Student;
void inputStudent(Student *s) {
printf("请输入学号: ");
scanf("%d", &s->id);
printf("请输入姓名: ");
scanf("%s", s->name);
printf("请输入成绩: ");
scanf("%f", &s->score);
}
void printStudent(Student s) {
printf("%d\t%s\t%.1f\n", s.id, s.name, s.score);
}
float calculateAverage(Student stu[], int n) {
float sum = 0;
for (int i = 0; i < n; i++) {
sum += stu[i].score;
}
return sum / n;
}
void findTop(Student stu[], int n) {
int maxIndex = 0;
for (int i = 1; i < n; i++) {
if (stu[i].score > stu[maxIndex].score) {
maxIndex = i;
}
}
printf("最高分学生: ");
printStudent(stu[maxIndex]);
}
int main() {
Student class[MAX_STUDENTS];
int count;
printf("请输入学生人数: ");
scanf("%d", &count);
for (int i = 0; i < count; i++) {
printf("\n输入第%d个学生信息:\n", i + 1);
inputStudent(&class[i]);
}
printf("\n学生信息:\n");
printf("学号\t姓名\t成绩\n");
for (int i = 0; i < count; i++) {
printStudent(class[i]);
}
printf("\n平均分: %.2f\n", calculateAverage(class, count));
findTop(class, count);
return 0;
}
struct Student { ... } 后面必须有分号。.,结构体指针用 ->。s1 = s2;),但浅拷贝,如果包含指针成员需注意。sizeof(结构体) 可能大于各成员大小之和。使用 #pragma pack 可控制对齐(谨慎使用)。结构体和联合体是C语言中组织复杂数据的重要工具。结构体用于组合不同类型的数据形成记录,联合体用于节省内存或实现多态。掌握它们的用法,可以为后续学习链表、树等数据结构打下坚实基础。 下一章我们将学习文件操作,实现数据的持久化存储。