一、结构体的定义与声明
结构体是一种 自定义数据类型,用于将多个不同类型的变量组合成一个整体。
1. 定义结构体类型
struct Student {char name[20];// 字符数组成员int age;// 整型成员float score;// 浮点型成员
};
2. 统一 typedef
定义
确保通过 typedef
定义的类型名全局唯一:
//在头文件中统一定义
typedef struct Point {int x;int y;
} Point;// struct Point 和 Point 类型一致
3. 声明结构体变量
// 方式1:先定义类型,再声明变量
struct Student stu1;// 方式2:定义类型时直接声明变量(了解)
struct Book {char title[50];float price;
} book1, book2;// 同时声明book1和book2
4.省略标签声明方式(声明后不可以定义变量)
// 省略标签,直接定义变量
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;
}
二、结构体的初始化
1. 直接初始化
在声明时直接赋值:
struct Student stu1 = {"Alice", 18, 95.5};
2. 按成员初始化(C99标准)
指定成员赋值,顺序可调:
struct Student stu2 = {.age = 20,.name = "Bob",.score = 88.0
};
3. 动态初始化
通过代码逐个赋值:
struct Student stu3;
strcpy(stu3.name, "Charlie");// 字符串需用strcpy
stu3.age = 22;
stu3.score = 92.5;
4. 结构体整体赋值仅限同类型结构体
结构体类型必须完全一致(包括成员顺序、类型和名称):
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;//正确
三、结构体成员的访问
1. 普通结构体变量
使用 .
运算符访问成员:
printf("姓名:%s,年龄:%d,分数:%.1f\n", stu1.name, stu1.age, stu1.score);
2. 结构体指针
使用 ->
运算符或 (*ptr).
访问成员:
struct Student *p = &stu1;
printf("姓名:%s\n", p->name);// 等价于 (*p).name
printf("年龄:%d\n", (*p).age);
四、结构体数组
1. 声明与初始化
struct Student class[3] = {{"Alice", 18, 95.5},{"Bob", 19, 88.0},{"Charlie", 20, 92.5}
};
2. 访问数组成员
for (int i = 0; i < 3; i++) {printf("学生%d:%s,%d岁,分数%.1f\n",i+1, class[i].name, class[i].age, class[i].score);
}
五、结构体与指针
1. 指向结构体的指针
struct Student stu = {"David", 21, 90.0};
struct Student *p = &stu;// 修改成员值
p->age = 22;
strcpy(p->name, "Dave");
2. 动态分配结构体内存
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);// 释放内存
}
六、结构体作为函数参数
1. 传递结构体副本
函数内修改不影响原结构体:
void print_student(struct Student s) {printf("姓名:%s\n", s.name);
}print_student(stu1);
2. 传递结构体指针
函数内可直接修改原结构体:
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等)
九、常见错误与注意事项
错误 | 说明 | 解决方法 |
---|---|---|
未初始化结构体 | 结构体成员可能包含随机值。 | 声明时初始化或手动赋值。 |
结构体赋值浅拷贝 | 直接赋值结构体时,指针成员指向同一内存。 | 深拷贝需手动复制指针指向的内容。 |
混淆. 和-> 运算符 |
对指针使用. 或对普通变量使用-> 。 |
明确指针用-> ,普通变量用. 。 |
内存对齐浪费空间 | 结构体成员顺序不合理导致内存浪费。 | 按成员大小降序排列减少填充。 |
十、示例代码
1. 动态创建结构体数组
#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;
}
2. 结构体与文件操作
#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;
}