解码C语言复合数据类型之结构体
结构体的定义与声明
结构体是一种 自定义数据类型,用于将多个不同类型的变量组合成一个整体。
定义结构体类型
struct Student {
char name[20];// 字符数组成员
int age;// 整型成员
float score;// 浮点型成员
};
统一 typedef
定义
确保通过 typedef
定义的类型名全局唯一:
//在头文件中统一定义
typedef struct Point {
int x;
int y;
} Point;// struct Point 和 Point 类型一致
声明结构体变量
// 方式1:先定义类型,再声明变量
struct Student stu1;
// 方式2:定义类型时直接声明变量(了解)
struct Book {
char title[50];
float price;
} book1, book2;// 同时声明book1和book2
省略标签声明方式(声明后不可以定义变量)
// 省略标签,直接定义变量
struct {
char name[50];
int age;
float height;
} person1, person2;// 直接声明变量
// 或者使用typedef创建别名
typedef struct {
char name[50];
int age;
float height;
} Person;// Person现在是类型名
Person person3;// 可以直接使用
使用省略标签的场景(嵌套结构体)
#include <stdio.h>
typedef struct {
// 内嵌匿名结构体,只在DateTime中使用,其他地方不能定义与date同型结构体
struct {
int hour;
int minute;
int second;
} time;
struct {
int year;
int month;
int day;
} date;
} DateTime;
int main() {
DateTime dt = {{12, 30, 45}, {2024, 1, 15}};
printf("时间: %02d:%02d:%02d\n",
dt.time.hour, dt.time.minute, dt.time.second);
return 0;
}
结构体的初始化
直接初始化
在声明时直接赋值:
struct Student stu1 = {"Alice", 18, 95.5};
按成员初始化(C99标准)
指定成员赋值,顺序可调:
struct Student stu2 = {
.age = 20,
.name = "Bob",
.score = 88.0
};
动态初始化
通过代码逐个赋值:
struct Student stu3;
strcpy(stu3.name, "Charlie");// 字符串需用strcpy
stu3.age = 22;
stu3.score = 92.5;
结构体整体赋值仅限同类型结构体
结构体类型必须完全一致(包括成员顺序、类型和名称):
struct PointA { int x; int y; };
struct PointB { int x; int y; };
struct PointA a = {1, 2};
struct PointB b = a;// 错误!虽然成员相同,但类型名不同
struct PointA c = a;//正确
结构体成员的访问
普通结构体变量
使用 .
运算符访问成员:
printf("姓名:%s,年龄:%d,分数:%.1f\n", stu1.name, stu1.age, stu1.score);
结构体指针
使用 ->
运算符或 (*ptr).
访问成员:
struct Student *p = &stu1;
printf("姓名:%s\n", p->name);// 等价于 (*p).name
printf("年龄:%d\n", (*p).age);
结构体数组
声明与初始化
struct Student class[3] = {
{"Alice", 18, 95.5},
{"Bob", 19, 88.0},
{"Charlie", 20, 92.5}
};
访问数组成员
for (int i = 0; i < 3; i++) {
printf("学生%d:%s,%d岁,分数%.1f\n",
i+1, class[i].name, class[i].age, class[i].score);
}
结构体与指针
指向结构体的指针
struct Student stu = {"David", 21, 90.0};
struct Student *p = &stu;
// 修改成员值
p->age = 22;
strcpy(p->name, "Dave");
动态分配结构体内存
struct Student *p_stu = (struct Student*)malloc(sizeof(struct Student));
if (p_stu != NULL) {
strcpy(p_stu->name, "Eve");
p_stu->age = 23;
p_stu->score = 85.5;
free(p_stu);// 释放内存
}
结构体作为函数参数
传递结构体副本
函数内修改不影响原结构体:
void print_student(struct Student s) {
printf("姓名:%s\n", s.name);
}
print_student(stu1);
传递结构体指针
函数内可直接修改原结构体:
void update_age(struct Student *p, int new_age) {
p->age = new_age;
}
update_age(&stu1, 19);// stu1.age变为19
结构体嵌套
结构体可以包含其他结构体作为成员:
struct Date {
int year;
int month;
int day;
};
struct Person {
char name[20];
struct Date birthday;// 嵌套结构体
};
// 初始化嵌套结构体
struct Person p = {"Tom", {2000, 5, 15}};
// 访问嵌套成员
printf("出生日期:%d年%d月%d日\n",
p.birthday.year, p.birthday.month, p.birthday.day);
结构体的大小与内存对齐
结构体的大小由成员大小和内存对齐规则决定,可通过 sizeof
获取:
struct Example {
char c;// 1字节
int i;// 4字节
double d;// 8字节
};
printf("结构体大小:%zu字节\n", sizeof(struct Example));// 输出可能是16(对齐到8字节)
注:规则
1.内存按结构体成员的先后顺序排序,当排到该成员时,其前面已开辟的空间字节数必须是该成员类型所占字节数的整数倍
2.整体空间必须是其最大成员类型所占字节的整数倍。
3.根据操作系统:指针(32位系统):4字节;指针(64位系统):8字节
通过 #pragma pack(n) 可强制指定对齐值(n为1、2、4、8等)
常见错误与注意事项
错误 | 说明 | 解决方法 |
---|---|---|
未初始化结构体 | 结构体成员可能包含随机值。 | 声明时初始化或手动赋值。 |
结构体赋值浅拷贝 | 直接赋值结构体时,指针成员指向同一内存。 | 深拷贝需手动复制指针指向的内容。 |
混淆. 和-> 运算符 |
对指针使用. 或对普通变量使用-> 。 |
明确指针用-> ,普通变量用. 。 |
内存对齐浪费空间 | 结构体成员顺序不合理导致内存浪费。 | 按成员大小降序排列减少填充。 |
示例代码
动态创建结构体数组
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student {
char name[20];
int age;
};
int main() {
int n = 3;
struct Student *class = (struct Student*)malloc(n * sizeof(struct Student));
if (class == NULL) {
printf("内存分配失败!\n");
return 1;
}
// 初始化
strcpy(class[0].name, "Alice");
class[0].age = 18;
strcpy(class[1].name, "Bob");
class[1].age = 19;
// 输出
for (int i = 0; i < n; i++) {
printf("学生%d:%s,%d岁\n", i+1, class[i].name, class[i].age);
}
free(class);
return 0;
}
结构体与文件操作
#include <stdio.h>
struct Student {
char name[20];
int age;
};
int main() {
FILE *fp = fopen("students.dat", "wb");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
struct Student stu = {"Tom", 20};
fwrite(&stu, sizeof(struct Student), 1, fp);// 写入文件
fclose(fp);
// 读取文件
fp = fopen("students.dat", "rb");
struct Student read_stu;
fread(&read_stu, sizeof(struct Student), 1, fp);
printf("读取数据:%s,%d岁\n", read_stu.name, read_stu.age);
fclose(fp);
return 0;
}