当前位置: 首页 > news >正文

C语言的结构体与联合体

C语言的结构体与联合体

结构体(struct)和联合体(union)是C语言中用于组合不同数据类型的数据结构。它们允许程序员将多个相关的数据项组合在一起,以便更好地组织和管理复杂的数据。枚举类型(enum)则用于定义一组具名的整型常量,增强代码的可读性和可维护性。掌握结构体、联合体和枚举类型的定义与使用,是编写高效、可扩展C程序的重要技能。


1 结构体的定义与使用

结构体是将不同类型的变量组合在一起的用户定义的数据类型。它用于表示一个实体的多个属性,便于管理和传递复杂的数据。

结构体的定义

语法

struct 结构体名 {数据类型 成员名1;数据类型 成员名2;// ...
};
  • struct:关键字,用于定义结构体。
  • 结构体名:结构体的名称,用于引用该结构体类型。
  • 成员:结构体内部的变量,每个成员可以是不同的数据类型。

示例

#include <stdio.h>// 定义一个表示学生信息的结构体
struct Student {char name[50];int age;float gpa;
};
结构体的声明与初始化

定义结构体类型后,可以声明结构体变量,并对其进行初始化。

示例

#include <stdio.h>
#include <string.h>// 定义结构体
struct Student {char name[50];int age;float gpa;
};int main() {// 声明并初始化结构体变量struct Student student1;strcpy(student1.name, "张三");student1.age = 20;student1.gpa = 3.8;// 输出结构体成员printf("姓名: %s\n", student1.name);printf("年龄: %d\n", student1.age);printf("GPA: %.2f\n", student1.gpa);return 0;
}

输出

姓名: 张三
年龄: 20
GPA: 3.80

详细解释

  • 使用strcpy函数复制字符串到结构体的name成员。
  • 通过点运算符(.)访问和修改结构体成员。
结构体的初始化方式
  1. 逐个成员赋值

    struct Student student2;
    strcpy(student2.name, "李四");
    student2.age = 22;
    student2.gpa = 3.5;
    
  2. 使用初始化列表

    struct Student student3 = {"王五", 21, 3.9};
    
  3. 部分初始化

    未初始化的成员将被自动初始化为零。

    struct Student student4 = {"赵六", 0, 0.0};
    
结构体的嵌套

结构体可以包含其他结构体作为成员,实现更复杂的数据结构。

示例

#include <stdio.h>
#include <string.h>// 定义地址结构体
struct Address {char city[50];char country[50];
};// 定义学生结构体,包含地址结构体
struct Student {char name[50];int age;float gpa;struct Address addr;
};int main() {struct Student student;strcpy(student.name, "孙七");student.age = 23;student.gpa = 3.7;strcpy(student.addr.city, "北京");strcpy(student.addr.country, "中国");// 输出结构体成员printf("姓名: %s\n", student.name);printf("年龄: %d\n", student.age);printf("GPA: %.2f\n", student.gpa);printf("城市: %s\n", student.addr.city);printf("国家: %s\n", student.addr.country);return 0;
}

输出

姓名: 孙七
年龄: 23
GPA: 3.70
城市: 北京
国家: 中国

详细解释

  • 结构体Student中嵌套了结构体Address,通过student.addr.city访问嵌套结构体的成员。
结构体类型定义与typedef

使用typedef可以简化结构体类型的定义,使得声明结构体变量时无需重复使用struct关键字。

示例

#include <stdio.h>
#include <string.h>// 使用typedef定义结构体类型
typedef struct {char name[50];int age;float gpa;
} Student;int main() {// 直接使用Student类型声明变量Student student1;strcpy(student1.name, "周八");student1.age = 24;student1.gpa = 3.6;printf("姓名: %s\n", student1.name);printf("年龄: %d\n", student1.age);printf("GPA: %.2f\n", student1.gpa);return 0;
}

输出

姓名: 周八
年龄: 24
GPA: 3.60

详细解释

  • 使用typedef将匿名结构体类型命名为Student,简化后续的变量声明。
注意事项
  • 成员访问:使用点运算符(.)访问结构体成员;如果通过指针访问结构体成员,使用箭头运算符(->)。

    struct Student *ptr = &student1;
    printf("姓名: %s\n", ptr->name);
    
  • 内存对齐:结构体成员的排列可能会导致内存对齐问题,影响内存使用效率。可以使用#pragma pack指令控制内存对齐,但需谨慎使用。


2 结构体数组与指针

结构体数组和结构体指针是管理多个结构体数据的常用方式,适用于存储和操作大量相关的数据项。

结构体数组

结构体数组是由相同类型的结构体元素组成的数组,用于存储多个结构体实例。

示例

#include <stdio.h>
#include <string.h>typedef struct {char name[50];int age;float gpa;
} Student;int main() {// 声明结构体数组,包含3个学生Student students[3] = {{"李雷", 21, 3.5},{"韩梅梅", 22, 3.8},{"小明", 20, 3.2}};// 遍历结构体数组并输出成员for(int i = 0; i < 3; i++) {printf("学生%d:\n", i + 1);printf("  姓名: %s\n", students[i].name);printf("  年龄: %d\n", students[i].age);printf("  GPA: %.2f\n\n", students[i].gpa);}return 0;
}

输出

学生1:姓名: 李雷年龄: 21GPA: 3.50学生2:姓名: 韩梅梅年龄: 22GPA: 3.80学生3:姓名: 小明年龄: 20GPA: 3.20

详细解释

  • 使用结构体数组students存储多个Student结构体实例。
  • 通过数组索引访问每个结构体元素,并使用点运算符访问其成员。
结构体指针

结构体指针是指向结构体变量的指针,通过指针可以访问和修改结构体的成员。

示例

#include <stdio.h>
#include <string.h>typedef struct {char name[50];int age;float gpa;
} Student;int main() {Student student = {"张华", 23, 3.9};Student *ptr = &student; // 指针指向结构体变量// 通过指针访问成员printf("姓名: %s\n", ptr->name);printf("年龄: %d\n", ptr->age);printf("GPA: %.2f\n", ptr->gpa);// 修改成员ptr->age = 24;ptr->gpa = 4.0;printf("\n修改后:\n");printf("姓名: %s\n", ptr->name);printf("年龄: %d\n", ptr->age);printf("GPA: %.2f\n", ptr->gpa);return 0;
}

输出

姓名: 张华
年龄: 23
GPA: 3.90修改后:
姓名: 张华
年龄: 24
GPA: 4.00

详细解释

  • 使用箭头运算符(->)通过指针访问和修改结构体成员。
  • 修改指针指向的结构体成员,实际修改了原结构体变量的值。
结构体数组与指针的结合使用

结构体数组与指针结合使用,可以高效地遍历和操作结构体数组。

示例

#include <stdio.h>
#include <string.h>typedef struct {char name[50];int age;float gpa;
} Student;int main() {// 声明结构体数组Student students[3] = {{"刘强", 25, 3.6},{"陈静", 22, 3.7},{"王磊", 24, 3.8}};// 声明结构体指针并指向数组的第一个元素Student *ptr = students;// 使用指针遍历结构体数组for(int i = 0; i < 3; i++) {printf("学生%d:\n", i + 1);printf("  姓名: %s\n", (ptr + i)->name);printf("  年龄: %d\n", (ptr + i)->age);printf("  GPA: %.2f\n\n", (ptr + i)->gpa);}return 0;
}

输出

学生1:姓名: 刘强年龄: 25GPA: 3.60学生2:姓名: 陈静年龄: 22GPA: 3.70学生3:姓名: 王磊年龄: 24GPA: 3.80

详细解释

  • 通过指针ptr和指针运算访问结构体数组的各个元素。
  • 使用(ptr + i)->member的方式访问每个结构体的成员。
注意事项
  • 数组越界:确保指针操作不超过结构体数组的边界,避免未定义行为。
  • 指针有效性:指针必须指向有效的结构体变量或数组元素,避免悬挂指针或野指针。

3 联合体的定义与应用

联合体(union)是与结构体类似的数据结构,但与结构体不同,联合体的所有成员共用同一块内存空间。这意味着在任意时刻,联合体只能存储一个成员的值。联合体适用于需要在同一内存位置存储不同类型数据的场景,如节省内存空间或实现数据类型转换。

联合体的定义

语法

union 联合体名 {数据类型 成员名1;数据类型 成员名2;// ...
};
  • union:关键字,用于定义联合体。
  • 联合体名:联合体的名称。
  • 成员:联合体内部的变量,共用同一内存空间。

示例

#include <stdio.h>// 定义一个联合体,用于存储不同类型的数据
union Data {int intValue;float floatValue;char charValue;
};int main() {union Data data;// 存储整数data.intValue = 100;printf("整数值: %d\n", data.intValue);// 存储浮点数,覆盖之前的整数值data.floatValue = 98.6;printf("浮点数值: %.1f\n", data.floatValue);// 存储字符,覆盖之前的浮点数值data.charValue = 'A';printf("字符值: %c\n", data.charValue);return 0;
}

输出

整数值: 100
浮点数值: 98.6
字符值: A

详细解释

  • 联合体Data的所有成员共用同一块内存空间。
  • 每次赋值给一个成员,会覆盖前一个成员的值。
  • 联合体的大小等于其最大成员的大小。
联合体的应用场景
  1. 内存节省:当一个数据结构的不同成员不会同时使用时,使用联合体可以节省内存空间。
  2. 数据类型转换:通过联合体可以实现不同数据类型之间的转换,如将浮点数的二进制表示解释为整数。
  3. 协议解析:在网络协议解析中,不同的数据字段可能需要不同的表示方式,使用联合体可以方便地处理。
联合体与结构体的区别
特性结构体(struct联合体(union
内存分配每个成员都有独立的内存空间所有成员共享同一块内存空间
大小所有成员大小之和(考虑内存对齐)最大成员的大小
成员访问可以同时访问所有成员只能正确访问最后一次赋值的成员
用途表示具有多个属性的实体表示不同类型的可选数据,节省内存空间
示例:使用联合体进行数据类型转换
#include <stdio.h>// 定义一个联合体,用于数据类型转换
union Converter {float f;unsigned int i;
};int main() {union Converter conv;conv.f = 3.14f;printf("浮点数: %.2f\n", conv.f);printf("对应的二进制表示: 0x%X\n", conv.i);// 通过整数成员访问浮点数的二进制表示conv.i = 0x4048F5C3;printf("整数: %u\n", conv.i);printf("对应的浮点数: %.2f\n", conv.f);return 0;
}

输出(具体二进制表示可能因系统不同):

浮点数: 3.14
对应的二进制表示: 0x4048F5C3
整数: 1078523331
对应的浮点数: 3.14

详细解释

  • 联合体Converter允许通过成员fi访问同一块内存。
  • 将浮点数赋值给f,然后通过i查看其二进制表示。
  • 通过直接赋值给i,再通过f解释该二进制表示为浮点数。
注意事项
  • 覆盖问题:赋值给联合体的一个成员会覆盖之前赋值的成员,导致其他成员的值不再有效。
  • 数据一致性:确保在使用联合体时,只访问最近赋值的成员,以避免未定义行为。
  • 内存对齐:联合体的内存对齐与结构体类似,需注意内存对齐对性能和正确性的影响。

4 枚举类型

枚举类型(enum)是用户定义的一种数据类型,用于表示一组具名的整型常量。枚举类型提高了代码的可读性和可维护性,使得程序员可以使用有意义的名称代替具体的数值。

枚举类型的定义

语法

enum 枚举名 {常量名1,常量名2,// ...常量名N
};
  • enum:关键字,用于定义枚举类型。
  • 枚举名:枚举类型的名称。
  • 常量名:枚举成员的名称,默认从0开始依次递增,可以手动指定值。

示例

#include <stdio.h>// 定义一个表示星期的枚举类型
enum Weekday {SUNDAY,    // 0MONDAY,    // 1TUESDAY,   // 2WEDNESDAY, // 3THURSDAY,  // 4FRIDAY,    // 5SATURDAY   // 6
};int main() {enum Weekday today;today = WEDNESDAY;printf("今天是星期%d\n", today); // 输出: 今天是星期3return 0;
}

输出

今天是星期3

详细解释

  • 枚举成员SUNDAYSATURDAY分别被赋予从06的整数值。
  • 通过枚举类型Weekday声明变量today,并赋值为WEDNESDAY
枚举成员的自定义值

可以为枚举成员手动指定整数值,后续成员值会自动递增。

示例

#include <stdio.h>// 定义一个带有自定义值的枚举类型
enum ErrorCode {SUCCESS = 0,ERROR_NOT_FOUND = 404,ERROR_SERVER = 500
};int main() {enum ErrorCode code;code = ERROR_NOT_FOUND;printf("错误代码: %d\n", code); // 输出: 错误代码: 404code = ERROR_SERVER;printf("错误代码: %d\n", code); // 输出: 错误代码: 500return 0;
}

输出

错误代码: 404
错误代码: 500

详细解释

  • 枚举成员SUCCESS被赋值为0ERROR_NOT_FOUND404ERROR_SERVER500
  • 可以根据需要为枚举成员指定具体的值,便于与外部系统或协议接口匹配。
枚举类型的使用

枚举类型可以用于控制流程、状态表示等场景,增强代码的可读性。

示例

#include <stdio.h>// 定义一个表示交通信号灯的枚举类型
typedef enum {RED,YELLOW,GREEN
} TrafficLight;int main() {TrafficLight light = RED;switch(light) {case RED:printf("停止!\n");break;case YELLOW:printf("准备!\n");break;case GREEN:printf("前进!\n");break;default:printf("未知信号灯状态。\n");}return 0;
}

输出

停止!

详细解释

  • 使用typedef简化枚举类型的声明,可以直接使用TrafficLight作为类型名。
  • 通过switch语句根据枚举成员执行不同的操作,提高代码的可读性和维护性。
枚举类型与整数的关系

枚举类型在C语言中本质上是整数类型,可以与整数进行互换,但需注意类型安全。

示例

#include <stdio.h>// 定义枚举类型
enum Color {RED = 1,GREEN,BLUE
};int main() {enum Color c = GREEN;int num = BLUE;printf("枚举成员 GREEN 的值: %d\n", c); // 输出: 2printf("整数 3 对应的枚举成员: %d\n", num); // 输出: 3// 比较枚举成员与整数if(c == 2) {printf("c 等于 2\n"); // 输出此行}return 0;
}

输出

枚举成员 GREEN 的值: 2
整数 3 对应的枚举成员: 3
c 等于 2

详细解释

  • 枚举成员GREEN被自动赋值为2BLUE3
  • 可以将枚举成员赋值给整数变量,反之亦然,但需确保值在枚举成员的定义范围内,以避免逻辑错误。
注意事项
  • 枚举类型的范围:枚举成员的值必须在整数类型的范围内。
  • 避免重复值:不同的枚举成员可以拥有相同的值,但应谨慎使用,以免引发混淆。
  • 类型安全:C语言中的枚举类型不是强类型,枚举变量可以与整数直接互换,但在大型项目中应注意类型安全,以防止错误。

总结

结构体、联合体和枚举类型是C语言中用于组织和管理复杂数据的强大工具。通过合理使用这些数据结构,程序员可以编写出更具结构性、可读性和可维护性的代码。本章详细介绍了结构体的定义与使用、结构体数组与指针、联合体的定义与应用以及枚举类型的定义与使用。以下是本章的关键点:

  • 结构体
    • 将不同类型的变量组合在一起,表示一个实体的多个属性。
    • 支持嵌套结构体,增强数据结构的表达能力。
    • 使用typedef简化结构体类型的声明。
  • 结构体数组与指针
    • 结构体数组用于存储多个结构体实例,适用于批量管理数据。
    • 结构体指针允许通过指针访问和修改结构体成员,提高操作灵活性。
    • 结构体数组与指针结合使用,可实现高效的数据遍历和操作。
  • 联合体
    • 所有成员共享同一块内存空间,适用于节省内存或实现数据类型转换。
    • 注意成员赋值覆盖问题,确保只访问最后赋值的成员。
    • 联合体与结构体相比,适用于不同类型数据互斥使用的场景。
  • 枚举类型
    • 定义一组具名的整型常量,增强代码可读性。
    • 可以为枚举成员指定具体的整数值,便于与外部系统接口。
    • 通过枚举类型实现状态表示、控制流程等功能,提高代码的逻辑清晰度。
http://www.dtcms.com/a/316214.html

相关文章:

  • LOOP Finance:一场 Web3 共和国中的金融制度实验
  • Spring Boot 与 Ollama 集成部署私有LLM服务 的完整避坑指南,涵盖 环境配置、模型管理、性能优化 和 安全加固
  • 【数据结构入门】数组和链表的OJ题(2)
  • uv与conda环境冲突,无法使用uv环境,安装包之后出现ModuleNotFoundError: No module named ‘xxx‘等解决方法
  • SpringBoot中策略模式使用
  • tcp 确认应答和超时时间
  • mq_timedsend系统调用及示例
  • Lua语言程序设计1:基础知识、数值、字符串与表
  • DDOS攻击和CC攻击对服务器的伤害有哪些?
  • 蘑兔音乐:音乐创作的神奇钥匙​
  • AI产品经理手册(Ch9-11)AI Product Manager‘s Handbook学习笔记
  • Linux系统交叉编译:依赖、构建与实践
  • makefile的使用与双向链表
  • 使用YOLOv8-gpu训练自己的数据集并预测
  • 多传感器融合
  • 2025暑期作业
  • 企业如何用现代数仓架构挖掘新业务盈利点?AllData产品从目标、路径、结果给出答案
  • 分布式文件系统06-分布式中间件弹性扩容与rebalance冲平衡
  • 集成学习与随机森林:从原理到实践指南
  • 解决VScode无法打开本地文件夹及远程连接后无反应的问题
  • Maven和Gradle在构建项目上的区别
  • 范式集团与海博思创成立合资公司,杀入“AI+储能”赛道
  • 机器学习之KNN、贝叶斯与决策树算法
  • 【题解】P3172 [CQOI2015] 选数(倍数莫反做法)
  • 深圳多奥500KG磁力锁(DAIC-MJ-500S)技术解析与产品优势报告,应用到门禁系统坚若磐石!
  • 计算机网络 第2章通信基础(竟成)
  • Pycaita二次开发基础代码解析:参数化模板创建与设计表驱动建模
  • 【Java面试题】注解,异常相关知识
  • Go语言的gRPC教程-错误处理
  • Android AppSearch 深度解析:现代应用搜索架构与实践