c语言初阶 结构体
结构体
- C语言结构体详解
- 1. 结构体的声明
- 2. 结构体的定义
- (1)结构体定义与变量的区别
- (2)结构体嵌套
- 3. 结构体的初始化
- 4. 结构体成员的访问
- (1)通过结构体变量访问(.操作符)
- (2)通过结构体指针访问(->操作符)
- (3)两种访问方式的使用场景
- 5. 结构体传参
- (1)传值调用
- (2)传地址调用
- (3)两种传参方式的对比
- 6. 一句话总结
C语言结构体详解
1. 结构体的声明
结构体是一些值的集合,这些值称为成员变量,每个成员可以是不同类型的变量。
声明格式:
struct 结构体名 {成员变量类型 成员变量名;// 更多成员变量...
};
示例:声明一个学生结构体
// 声明学生结构体
struct Student {char name[20]; // 姓名int age; // 年龄float score; // 成绩
};
用结构体实例化的两种情况:
- 声明结构体后实例化
struct Student {char name[20];int age;float score;
};// 实例化结构体变量
struct Student stu1;
struct Student stu2;
- 声明结构体的同时实例化
struct Student {char name[20];int age;float score;
} stu1, stu2; // 声明时直接实例化变量
2. 结构体的定义
(1)结构体定义与变量的区别
- 结构体定义:描述结构体的组成,不分配内存空间
- 结构体变量:根据结构体定义创建的具体实例,会分配内存空间
示例:
// 结构体定义(不占空间)
struct Book {char title[50];float price;int page;
};// 结构体变量(占空间)
struct Book b1; // 分配内存,大小为50 + 4 + 4 = 58字节(不考虑内存对齐)
(2)结构体嵌套
结构体的成员可以是另一个结构体。
示例:
// 声明日期结构体
struct Date {int year;int month;int day;
};
// 声明图书结构体,嵌套日期结构体
struct Book {char title[50];float price;struct Date publish_date; // 嵌套结构体作为成员
};
// 实例化嵌套结构体变量
struct Book b1;
3. 结构体的初始化
使用{}
对结构体变量进行初始化,顺序与结构体成员声明顺序一致。
示例1:基本初始化
struct Student {char name[20];int age;float score;
};\\完全初始化
struct Student stu1 = {"张三", 18, 90.5f};
\\不完全初始化(未初始化的成员自动为0或空)struct Student stu2 = {"李四", 19}; // score默认为0.0f
示例 2:嵌套结构体初始化
struct Date {int year;int month;int day;
};struct Book {char title[50];float price;struct Date publish_date;
};// 嵌套初始化,内部结构体用{}包裹
struct Book b1 = {"C语言教程", 39.9f, {2023, 10, 15}};
示例 3:指定成员初始化(C99 支持)
struct Student stu = {.age = 20,.name = "王五", // 可以不按声明顺序.score = 88.0f
};
4. 结构体成员的访问
(1)通过结构体变量访问(.操作符)
当直接使用结构体变量时,用.
操作符访问成员。
格式:结构体变量名.成员名
示例:
struct Student {char name[20];int age;float score;
};
int main() {struct Student stu = {"张三", 18, 90.5f};// 使用.操作符访问成员printf("姓名:%s\n", stu.name);printf("年龄:%d\n", stu.age);// 修改成员值stu.age = 19;stu.score = 92.0f;printf("修改后年龄:%d\n", stu.age);return 0;
}
(2)通过结构体指针访问(->操作符)
当使用结构体指针时,用->
操作符访问成员。
格式:结构体指针->成员名
示例:
struct Student {char name[20];int age;float score;
};
int main() {struct Student stu = {"张三", 18, 90.5f};struct Student *p = &stu; // 定义结构体指针,指向stu// 使用->操作符访问成员printf("姓名:%s\n", p->name);printf("年龄:%d\n", p->age);// 修改成员值p->age = 19;p->score = 92.0f;printf("修改后年龄:%d\n", p->age);return 0;
}
等价写法:(*指针变量).成员名
printf("姓名:%s\n", (*p).name); // 与p->name等价
(3)两种访问方式的使用场景
- .操作符:适用于直接操作结构体变量的场景,代码更简洁直观
- ->操作符:适用于通过指针操作结构体的场景,尤其在函数传参为结构体指针时使用
示例:函数中访问结构体成员
// 使用.操作符(需要结构体变量)
void print_stu(struct Student s) {printf("姓名:%s,年龄:%d\n", s.name, s.age);
}
// 使用->操作符(需要结构体指针)
void print_stu_ptr(struct Student *p) {printf("姓名:%s,年龄:%d\n", p->name, p->age);
}
5. 结构体传参
结构体传参分为传值和传地址两种方式。
(1)传值调用
将结构体变量作为参数传递,函数内部会创建一个结构体副本。
示例:
struct Student {char name[20];int age;float score;
};
// 传值调用,接收结构体变量
void modify_stu(struct Student s) {s.age = 20; // 仅修改副本,不影响原变量s.score = 95.0f;
}
int main() {struct Student stu = {"张三", 18, 90.5f};modify_stu(stu); // 传结构体变量printf("年龄:%d\n", stu.age); // 输出18,原变量未被修改return 0;
}
特点:
- 函数内部修改不会影响原结构体变量
- 传递大结构体时,会占用较多内存和时间(拷贝整个结构体)
(2)传地址调用
将结构体指针作为参数传递,函数内部通过指针访问原结构体。
示例:
struct Student {char name[20];int age;float score;
};
// 传地址调用,接收结构体指针
void modify_stu_ptr(struct Student *s) {s->age = 20; // 通过指针修改原变量s->score = 95.0f;
}
int main() {struct Student stu = {"张三", 18, 90.5f};modify_stu_ptr(&stu); // 传结构体地址printf("年龄:%d\n", stu.age); // 输出20,原变量被修改return 0;
}
特点:
- 函数内部修改会影响原结构体变量
- 仅传递 4 字节(32 位系统)的指针,节省内存和时间
- 更推荐使用的传参方式,尤其对于大型结构体
(3)两种传参方式的对比
传参方式 | 实现方式 | 内存占用 | 能否修改原结构体 | 适用场景 |
---|---|---|---|---|
传值调用 | 传递结构体变量 | 整个结构体大小 | 不能 | 小型结构体,无需修改原结构体 |
传址调用 | 传递结构体指针 | 4字节(32位 | 能 | 所有场景,尤其大型结构体 |
推荐做法:优先使用传地址调用,既节省资源,又能灵活控制是否修改原结构体(如需保护原结构体,可在函数参数前加const
)。
示例:保护原结构体的传址调用
// 加const修饰,防止修改原结构体
void print_stu(const struct Student *s) {printf("姓名:%s,年龄:%d\n", s->name, s->age);// s->age = 20; // 错误,不能修改const结构体
}
6. 一句话总结
内容 | 关键点 |
---|---|
结构体声明 | 描述结构体组成,用struct 结构体名 {成员列表}; 格式 |
结构体定义与变量 | 定义不占空间,变量占空间,变量是结构体的具体实例 |
结构体初始化 | 用{} 初始化,嵌套结构体需嵌套{},可不完全初始化 |
成员访问 | 变量用.操作符 ,指针用->操作符 ,p->member 等价于(*p).member |
结构体传参 | 传值调用不修改原变量,传址调用效率高且可修改原变量,推荐传址调用 |