C语言集锦 | 03 - C语言的复合数据类型(typedef关键字、结构体、枚举体、共用体)
文档版本 | 更新时间 | 更新内容 |
---|---|---|
v1.0 | 2020-09-14 | 初稿完成 |
文章目录
- 一、typedef关键词
- 二、结构体(重点)
- 1. 为什么需要结构体
- 2. 什么是结构体
- 3. 如何定义结构体
- 4. 如何使用结构体
- 4.1. 赋值和初始化
- 4.2. 访问每个成员
- 4.3. 结构体变量的运算
- 4.4. 结构体作为函数传递问题
- 5. 结构体内存对齐问题(面试常考)
- 5.1. 问题描述
- 5.2. 问题原因
- 5.3. 问题分析
- 6. 结构体数组
- 6.1. 静态数组
- 6.2. 动态数组
- 三、枚举体
- 1. 什么是枚举体
- 2. 如何定义枚举体
- 3. 如何使用枚举体
- 四. 共用体
- 1. 什么叫做共用体
- 2. 为什么需要共用体
- 3. 如何定义共用体
- 4. 如何使用共用体
一、typedef关键词
typedef用来给数据类型起别名,用法如下:
typedef <已有数据类型> <新名称>;
比如:
typedef unsingned char uint8_t;
typedef unsingned int uint16_t;
二、结构体(重点)
1. 为什么需要结构体
为了表示一些复杂的事物,普通数据类型无法满足要求。
2. 什么是结构体
把一些基本数据类型组合在一起而形成的一个新的数据类型,叫做结构体。
3. 如何定义结构体
定义结构体有四种方法:
① 只定义数据类型,不定义变量:
struct student_st {char name[20]; /* 学生名称 */char sex; /* 学生性别 */float score; /* 学生成绩 */
};
② 定义数据类型,同时定义一个变量:
struct student_st {char name[20]; /* 学生名称 */char sex; /* 学生性别 */float score; /* 学生成绩 */
} st1;
③ 定义数据类型,同时定义一个变量,但结构体类型匿名:
struct {char name[20]; /* 学生名称 */char sex; /* 学生性别 */float score; /* 学生成绩 */
} st1;
④ 定义数据类型,不定义变量,同时为新的类型起个别名(多用):
typedef struct student_st {char name[20]; /* 学生名称 */char sex; /* 学生性别 */float score; /* 学生成绩 */
} student_t;
4. 如何使用结构体
4.1. 赋值和初始化
结构体变量只能在定义的同时赋初值:
student_t st1 = {"mculover666", 'M', 66.6};
一旦定义完成,将无法整体赋值,只能单个赋值。
4.2. 访问每个成员
访问结构体中的成员有两种方法:
- 通过结构体变量访问其中的成员;
- 通过指向结构体变量的指针访问其中的成员;
① 通过结构体变量访问:
printf("name:%s sex:%c score:%.1f\r\n", st1.name, st1.sex, st1.score);
② 通过指向结构体变量的指针访问:
student_t* st_ptr = &st1;
printf("name:%s sex:%c score:%.1f\r\n", st_ptr->name, st_ptr->sex, st_ptr->score);
4.3. 结构体变量的运算
结构体变量之间不能相加、相减、也不能相互乘除,但是结构体变量之间可以相互赋值。
student_t st1 = {"mculover666", 'M', 66.6};
student_t st2;
st2 = st1;
printf("name:%s sex:%c score:%.1f\r\n", st2.name, st2.sex, st2.score);
4.4. 结构体作为函数传递问题
如果要将结构体作为函数参数传递,两种方法如下:
- 直接传递结构体变量:栈的开销大,而且函数中无法做到对结构体的修改;
- 直接传递结构体指针:只是四个字节(或者8个字节)的指针,并且函数中可以对结构体修改。
5. 结构体内存对齐问题(面试常考)
5.1. 问题描述
执行如下代码:
printf("%d + %d + %d = %d", sizeof(st1.name), sizeof(st1.sex), sizeof(st1.score), sizeof(st1));
运行结果为:
20 + 1 + 4 = 28
很神奇,结构体不应该占用25个字节吗?为什么会是28个字节?
将st1每个成员的地址打印出来便知:
printf("st1: %p\nst1.name: %p\nst1.sex: %p\nst1.score:%p\n", &st1, &st1.name, &st1.sex, &st1.score);
打印结果为:
st1: 000000000061FDF0
st1.name: 000000000061FDF0
st1.sex: 000000000061FE04
st1.score:000000000061FE08
不难发现,sex变量是char类型,本来应该占用 1 个字节,实际却占用了 4 个字节!
5.2. 问题原因
编译器给结构体中的变量分配空间时有内存对齐规则:
① 结构体变量的起始地址能够被其最宽的成员大小整除;
② 结构体每个成员相对于起始地址的偏移,能够被其自身大小整除,如果不能,则在前一个成员后面补充空白字节;
③ 结构体总体大小能够被最宽的成员的大小整除,否则将在后面补充空白字节。
5.3. 问题分析
第2个成员sex是char类型,只占用一个字节,所以当后面的成员score定义时,其自身大小是4,结果发现000000000061FE04+1
不能满足对齐规则,所以补充空白字节,使前面有20+4=24个字节才行,地址变为000000000061FE08
。
如果我们手动补充上这些空白字节,则整个结构体大小还会是28个字节。
typedef struct student_st {char name[20]; /* 学生名称 */char sex; /* 学生性别 */char reserve_bytes[3]; /* 保留字节 */float score; /* 学生成绩 */
} student_t;
6. 结构体数组
6.1. 静态数组
student_t st[100];
6.2. 动态数组
#define STUDENT_NUM 100/* 申请动态内存 */
student_t* st_ptr = (student_t *)malloc(STUDENT_NUM * sizeof(student_t));/* 使用... *//* 使用完毕之后释放 */
free(st_ptr);
st_ptr = NULL;
三、枚举体
1. 什么是枚举体
枚举体是将一个类型的变量所有可能的值都罗列出来,并且此类型变量不能赋其它值。
2. 如何定义枚举体
上述结构体中有一个成员是sex(性别),适合使用枚举体。
定义枚举体方法有二。
① 只定义枚举类型:
enum student_sex_en {MALE = 'M',FAMALE = 'F',
};
② 定义枚举类型的同时,起个新名字,方便使用:
typedef enum student_sex_en {MALE = 'M',FAMALE = 'F',
} student_sex_t;
3. 如何使用枚举体
首先优化之前我们定义的结构体:
typedef struct student_st {char name[20]; /* 学生名称 */student_sex_t sex; /* 学生性别 */char reserve_bytes[3]; /* 保留字节 */float score; /* 学生成绩 */
} student_t;
接着在给sex变量赋值的时候使用:
student_t st1 = {"mculover666", MALE, 66.6};
四. 共用体
1. 什么叫做共用体
共用体就是多个变量共用同一段内存空间,共用体的大小是最大变量占用的内存空间。
2. 为什么需要共用体
最典型的一个用法就是:结构体无差异化遍历。
比如现在有一个结构体:
typedef struct num_st {int a;int b;int c;int d;
} num_t;
使用情况如下:
- 赋值时要求能在一个循环中遍历赋值,忽略成员的名字不同,这就叫做无差异化遍历;
- 使用时要求能按照成员名字访问;
显然,第一个需求适合使用数组,第二个需求适合使用结构体,所以就使用共用体:让数组和结构体共用同一个内存空间。
3. 如何定义共用体
按照上述需求定义共用体,并起个新名字,这个共用体占用的空间为4个int的大小:
typedef union num_un {num_t num;int n[4];
} num_u;
4. 如何使用共用体
在赋值时按照数组来用,直接循环遍历,满足第一个需求:
num_u num1;for (int i = 0; i < 4; i++) {num1.n[i] = i;}
在访问数据时按照结构体来用,按照名字取值,满足第二个需求:
printf("a = %d, b = %d, c = %d, d = %d\r\n", num1.num.a, num1.num.b, num1.num.c, num1.num.d);
最终运行结果为:
a = 0, b = 1, c = 2, d = 3
C语言集锦 | 03 - C语言的复合数据类型(typedef关键字、结构体、枚举体、共用体)相关推荐
- c语言结构体加联合,C语言:结构体和联合体(共用体)
结构体:struct 1.结构体变量的首地址能够被其最宽基本类型成员的大小所整除. 2.结构体每个成员相对于结构体首地址的偏移量(offset)都是成员的整数倍. 3.结构体的总大小为结构体最宽基本类 ...
- 春晚黑科技全盘点:刘德华、周杰伦到底来没来?Ubuntu linux c复合数据类型--结构体
本文转载自IT之家,刘德华.周杰伦到底有没有来春晚? 很多看了 2021 年央视春晚的人会对这个问题感兴趣--其实,周杰伦.刘德华是通过 AR.云技术 "出现"在现场的. 央视春晚 ...
- 黑马程序员C语言基础(第八天)复合类型(自定义类型)(结构体)、共用体(联合体)、枚举enum、 typedef
黑马程序员C语言基础(第一天) 黑马程序员C语言基础(第二天) 黑马程序员C语言基础(第三天) 黑马程序员C语言基础(第四天)数据类型 黑马程序员C语言基础(第五天)运算符与表达式.程序流程结构.数组 ...
- 【零基础学C语言】知识总结八:struct 结构体与 union 共用体
struct 结构体 struct即结构体,C程序中经常需要用相关的不同类型的数据来描述一个数据对象.例如,描述学生的综合信息时,需要使用学生的学号.姓名.性别等不同类型的数据时,像这种数据类型总是在 ...
- C语言共用体中,共用体变量的规律
运行以下代码,看一看输出的结果是什么? #include <stdio.h> int main(){union key{int k;char ch[2];}u;u.k = 258;prin ...
- C语言复习03(小甲鱼版本)
单链表 int main(void){struct Test{int x;int y;//错误写法:struct Test test;struct Test *test;//正确写法,指针指向结构体} ...
- c语言笔记照片_c语言笔记
如何看懂一个程序,分三步 1. 流程 2. 每个语句的功能 3. 试数 对于一些小程序的算法 尝试自己去编程解决它,大部分人都无法自己解决 如果解决不了,尝试看答案 关键要把答案看懂,这个要花很大的精 ...
- C语言:结构体和共用体
这是很基础的教程,我只是写给自己看,作为一个学习笔记记录一下,如果正在阅读的你觉得简单,请不要批评,可以关掉选择离开 如何学好一门编程语言 掌握基础知识,为将来进一步学习打下良好的基础. 上机实践,通 ...
- 【c语言基础必备】共用体union
共用体是C 语言的另外一种构造类型,与前面介绍的结构体类似.共用体也由基本数据结构组合而成,但是共用体和结构体却有本质区别,因为结构体中的每个成员都占用存储单元,所以结构体所占用的内存大小为所有成员各 ...
最新文章
- 【Python项目】CMDB的搭建12(SaltStack配置小结2/2)
- 云栖·追踪云原生|Serverless入围阿里云十大核心技术
- pandans导出Excel并将数据保存到不同的Sheet表中
- 181. maven项目ssm(父工程 子工程)
- 天池 在线编程 删除字符(单调栈)
- python合并单元格居中_Python实现Excel自动分组合并单元格
- Vulkan教程 - 18 阶段性总结
- Java读写NFC标签Ntag2x芯片源码
- 期货反跟单软件介绍与反跟单交易中的骗局
- 《计算机网络教程》(微课版 第五版)复习补充题
- 【原创】VBA学习笔记(6)VBE编辑器,VBA工程,各种窗口
- 一年级有计算机教学吗,一年级信息技术教学计划范文
- Importance Weighted Adversarial Nets for Partial Domain Adaptation
- 解决 cp: omitting directory ‘./dist’ ( 拷贝失败 )
- 信息收集--关联信息及漏洞情报
- 与200位专家相约,看国内外最新研发经验
- Github上一个优秀的Python学习资源:AwesomePythonResource
- IT项目开发团队建设与管理总结(转)
- ps制作双重曝光海报
- 笔记本外接显示屏模糊解决办法,调缩放比没用?
热门文章
- 关于Endnote导入文献以及将参考文献插入到word文件的简单介绍
- mysql 导入数据 1215,导入sql出现:#1215 – Cannot add foreign key constraint错误的解决办法...
- ARM9 2410移植之ARM中断原理, 中断嵌套的误区,中断号的怎么来的
- Spring Boot实战:整合Redis、MyBatis,封装RedisUtils工具类
- Gentoo Linux USE标记的使用含义
- 【英文原著 01】-The magician's nephew
- R01 - 005、HBase实时查询的原理
- 赛码网编程题---打字(C++)
- 【分布式能源的选址与定容】基于非支配排序多目标遗传优化算法求解分布式能源的选址与定容(Matlab代码实现)
- 达摩院 2023 十大科技趋势发布