结构体基础回顾
让我们简单回顾一下 struct 的定义。struct 是一种将不同类型的数据组合在一起形成单个复合类型的方式。

(图片来源网络,侵删)
// 定义一个名为 Person 的结构体
struct Person {
char name[50];
int age;
float height;
};
初始化的几种主要方法
初始化 struct 就是在创建变量的同时,为其成员赋予初始值,主要有以下几种方法:
顺序初始化(C99 之前和之后都适用)
这是最传统、最常见的方法,你按照结构体成员在定义中声明的顺序,用一对花括号 括起来,并用逗号 分隔各个成员的初始值。
语法:
struct StructName variableName = { value1, value2, value3, ... };
示例:

(图片来源网络,侵删)
#include <stdio.h>
#include <string.h>
struct Person {
char name[50];
int age;
float height;
};
int main() {
// 顺序初始化
struct Person p1 = { "张三", 30, 1.75f };
printf("姓名: %s\n", p1.name);
printf("年龄: %d\n", p1.age);
printf("身高: %.2f\n", p1.height);
return 0;
}
输出:
姓名: 张三
年龄: 30
身高: 1.75
优点:
- 简单直观,代码简洁。
- 在 C99 标准之前,这是唯一一种标准化的初始化方式。
缺点:
- 不灵活:如果结构体定义发生了变化(比如新增或删除成员),或者你想只初始化后面的成员而跳过前面的成员,这种方法就会出错或变得非常麻烦。
指定初始化(C99 及以后推荐)
C99 标准引入了“指定初始化器”(Designated Initializers),这是一个非常强大的特性,它允许你通过成员的名称来指定要初始化的成员,从而摆脱了顺序的限制。
语法:
struct StructName variableName = { .memberName1 = value1, .memberName2 = value2, ... };
示例 1:按任意顺序初始化
#include <stdio.h>
struct Person {
char name[50];
int age;
float height;
};
int main() {
// 指定初始化,顺序可以任意
struct Person p2 = { .age = 25, .height = 1.68f, .name = "李四" };
printf("姓名: %s\n", p2.name);
printf("年龄: %d\n", p2.age);
printf("身高: %.2f\n", p2.height);
return 0;
}
输出:
姓名: 李四
年龄: 25
身高: 1.68
示例 2:部分初始化(跳过某些成员) 这是指定初始化器最显著的优势,你可以只初始化你关心的成员,其他未指定的成员会被自动初始化为零。
#include <stdio.h>
struct Person {
char name[50];
int age;
float height;
};
int main() {
// 只初始化 age 和 height,name 会被自动初始化为空字符串 '\0'
struct Person p3 = { .age = 40, .height = 1.80f };
printf("姓名: \"%s\"\n", p3.name); // 打印空字符串
printf("年龄: %d\n", p3.age);
printf("身高: %.2f\n", p3.height);
return 0;
}
输出:
姓名: ""
年龄: 40
身高: 1.80
示例 3:嵌套结构体的指定初始化 指定初始化器也可以用于嵌套的结构体。
#include <stdio.h>
struct Point {
int x;
int y;
};
struct Rectangle {
struct Point top_left;
struct Point bottom_right;
char color[20];
};
int main() {
// 嵌套结构体的指定初始化
struct Rectangle rect = {
.top_left = { .x = 10, .y = 20 },
.bottom_right = { .x = 50, .y = 40 },
.color = "蓝色"
};
printf("左上角: (%d, %d)\n", rect.top_left.x, rect.top_left.y);
printf("右下角: (%d, %d)\n", rect.bottom_right.x, rect.bottom_right.y);
printf("颜色: %s\n", rect.color);
return 0;
}
输出:
左上角: (10, 20)
右下角: (50, 40)
颜色: 蓝色
优点:
- 非常灵活:可以按任意顺序初始化成员。
- 可读性强:代码一目了然,清楚地知道哪个值赋给了哪个成员。
- 安全性高:结构体定义修改后(只要初始化的成员还在),代码通常不需要修改。
- 部分初始化:未指定的成员自动为零,避免了未初始化变量的风险。
先声明,后逐个赋值
这种方法不算是“初始化”,而是“赋值”,它适用于所有 C 标准,先声明一个结构体变量,然后通过点操作符 逐个访问成员并赋值。
语法:
struct StructName variableName; variableName.memberName1 = value1; variableName.memberName2 = value2; // ...
示例:
#include <stdio.h>
#include <string.h> // 需要 strcpy 来给字符数组赋值
struct Person {
char name[50];
int age;
float height;
};
int main() {
// 先声明
struct Person p4;
// 后逐个赋值
strcpy(p4.name, "王五"); // 不能用 p4.name = "王五";
p4.age = 35;
p4.height = 1.82f;
printf("姓名: %s\n", p4.name);
printf("年龄: %d\n", p4.age);
printf("身高: %.2f\n", p4.height);
return 0;
}
注意:
- 对于字符数组(如
name),不能直接用 赋值,必须使用strcpy函数。 - 这种方法代码冗长,如果需要一次性设置多个值,不如初始化方法简洁。
在函数中返回初始化的结构体
在 C99 之前,函数不能直接返回一个结构体(因为结构体可能很大,按值返回效率低),通常的做法是:
- 创建一个结构体变量。
- 在函数内部初始化它。
- 将该变量的地址作为参数传递给函数,函数通过指针来修改它。
示例:
#include <stdio.h>
struct Point {
int x;
int y;
};
// 通过指针来修改结构体
void create_point(struct Point* p, int x, int y) {
p->x = x;
p->y = y;
}
int main() {
struct Point p1;
create_point(&p1, 100, 200);
printf("点坐标: (%d, %d)\n", p1.x, p1.y);
return 0;
}
C99 及以后: C99 引入了复合字面量(Compound Literals),使得函数可以更方便地返回一个结构体。
#include <stdio.h>
struct Point {
int x;
int y;
};
// 返回一个新创建的点
struct Point create_point(int x, int y) {
// (struct Point){x, y} 就是一个复合字面量,它创建一个临时的、已初始化的 Point 结构体
return (struct Point){x, y};
}
int main() {
struct Point p2 = create_point(300, 400);
printf("点坐标: (%d, %d)\n", p2.x, p2.y);
return 0;
}
总结与最佳实践
| 方法 | 适用标准 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|---|
| 顺序初始化 | 所有 C 标准 | 简单、代码短 | 不灵活,易受结构体定义影响 | 简单、固定场景的初始化;C99 之前的代码。 |
| 指定初始化 | C99 及以后 | 灵活、可读性强、安全 | 需要较新的编译器支持 | 强烈推荐,现代 C 语言开发的首选方法。 |
| 先声明后赋值 | 所有 C 标准 | 灵活(可在运行时决定值) | 代码冗长,效率稍低 | 变量需要在多个地方赋值,或值在运行时才能确定。 |
| 函数返回/修改 | 所有 C 标准 | 封装逻辑 | C99 之前语法较繁琐 | 需要将结构体的创建逻辑封装在函数中。 |
核心建议:
- 如果你使用的是支持 C99 或更高标准的编译器(如 GCC, Clang, MSVC 默认模式),请务必使用“指定初始化器”,这是现代 C 语言编程的最佳实践。
- 在进行大规模项目开发时,保持代码的可读性和可维护性至关重要,指定初始化器在这方面完胜顺序初始化。
- 如果只需要给一个或两个成员赋值,或者值是动态计算的,使用“先声明后赋值”的方法更合适。
