C语言结构体(struct)超级详解
从最基础到最容易被面试官问到的进阶用法,一次性讲透!
1. 结构体最基础的概念
struct 结构体标签名
{
类型1 成员名1;
类型2 成员名2;
...
类型N 成员名N;
};
最常用的三种写法(请牢牢记住这三种写法)
// 写法1(最常用、最推荐)
struct Student
{
char name[64];
int age;
float score;
char gender;
};
// 写法2(定义类型的同时直接创建变量)
struct Student
{
char name[64];
int age;
float score;
} stu1, stu2, stu3; // 同时创建了3个变量
// 写法3(匿名结构体 —— 只能用一次)
struct
{
char name[64];
int age;
} person1, person2; // 这种方式最不推荐
2. 结构体变量的几种常见定义方式对比(重要!)
| 写法 | 是否有类型名 | 可不可以多次创建变量 | 推荐指数 | 备注 |
|---|---|---|---|---|
struct Student s1; | 有(Student) | 可以 | ★★★★★ | 最推荐 |
struct Student s1 = {..}; | 有 | 可以 | ★★★★ | 初始化写法 |
struct {..} s1; | 没有 | 只能用一次 | ★☆ | 几乎不用 |
typedef struct {..} Stu; | 有(Stu) | 可以 | ★★★★★ | 非常常用!后面会讲 |
typedef struct Student Stu; | 有(Stu是别名) | 可以 | ★★★★ | 也很常见 |
3. 结构体内存对齐(★★★面试最爱考)
最核心的两句话:
- 结构体内存对齐的根本目的:提高CPU读取数据的效率(以牺牲一点内存为代价)
- 对齐原则(默认规则,绝大多数编译器都遵守):
每个成员相对于结构体首地址的偏移量 必须 是 自身大小 或者 编译器默认对齐数 的整数倍
整个结构体的大小 必须 是 所有成员中最大对齐数的整数倍
常用的对齐数(32/64位系统通常都是这样)
| 类型 | 自身大小 | 默认对齐数(常见) |
|---|---|---|
| char | 1 | 1 |
| short | 2 | 2 |
| int,float | 4 | 4 |
| double | 8 | 8 |
| 指针 | 4/8 | 4/8 |
| 结构体/共用体 | — | 看最大成员的对齐数 |
经典例题必做(请全部手算一遍)
struct A
{
char a; // 1
int b; // 4
double c; // 8
short d; // 2
};
struct B
{
double c; // 8
int b; // 4
char a; // 1
short d; // 2
};
struct C
{
char a; //1
struct A b; // 按照 struct A 整体来算
};
答案(请自己先算,再对):
- sizeof(struct A) = 24
- sizeof(struct B) = 16(最优写法)
- sizeof(struct C) = 48(因为里面嵌套了一个24字节的struct A)
4. 结构体初始化最常见的几种写法
struct Student
{
char name[64];
int age;
float score;
};
// 方式1 —— 推荐(C99之后支持)
struct Student s1 = {
.name = "张三",
.age = 20,
.score = 88.5f
};
// 方式2 —— 顺序初始化(最原始)
struct Student s2 = {"李四", 21, 92.5f};
// 方式3 —— 部分初始化(其余为0)
struct Student s3 = {"王五"};
// 方式4 —— 清零(最常用初始化方式)
struct Student s4 = {0};
5. 结构体指针最常用写法(非常非常重要!)
struct Student
{
char name[64];
int age;
} stu;
// 方式1(最常用、最推荐)
struct Student *p = &stu;
// 两种等价写法(一定要非常熟练)
(*p).age = 18; // 方式A
p->age = 18; // 方式B ← 99%的情况下都用这个!
6. 位域(Bit Field) —— 省内存神器(面试中高级常考)
struct Flags
{
unsigned int flag1 : 1; // 只占1bit
unsigned int flag2 : 1;
unsigned int flag3 : 2; // 占2bit
unsigned int flag4 : 4;
// 总共占用 1+1+2+4 = 8 bit = 1 byte
};
struct Flags f = {0};
f.flag1 = 1;
f.flag3 = 3;
位域限制(一定要记住):
- 不能取地址(&操作)
- 不能是double、float、long long
- 不能跨越存储单元(不同编译器可能不同)
7. 结构体与typedef的黄金搭配(最最常用写法)
// 写法1(最推荐!几乎所有现代C代码都这么写)
typedef struct
{
char name[64];
int age;
float score;
} Student;
// 使用方式
Student s1; // 直接用类型名,不用再写struct
Student *p = &s1;
p->age = 18;
// 写法2(也很常见)
typedef struct Student Student;
// 写法3(最原始,但现在很少用了)
struct Student
{
...
} typedef Student;
8. 结构体经典面试/笔试高频题型汇总
- 求sizeof(结构体) (内存对齐最重要)
- 成员偏移量怎么求?(offsetof宏)
- 结构体里面嵌套结构体怎么对齐?
- 位域怎么用?位域的内存布局是怎样的?
- 结构体作为函数参数是传值还是传指针?(99.9%用指针)
- 结构体数组、结构体指针数组、指针数组的区别?
- 结构体和共用体(union)在内存占用上的本质区别?
最后送你一句最重要的话:
“只要涉及到结构体,就一定要想到内存对齐”
只要写到结构体指针,就一定要用 -> 而不是 .
只要定义结构体类型,强烈建议搭配 typedef 使用
需要我帮你把哪几种写法、哪几道经典题再展开讲得更细一点?
或者想看某一类题目(比如内存对齐)的10道经典例题?直接告诉我~