C语言集锦 | 03 - C语言的复合数据类型(typedef关键字、结构体、枚举体、共用体) 【鸿蒙OS最新进展】鸿蒙内核liteos-a体验 在ART-Pi H750上移植TouchGFX(一)——使用STM32CUBMX生成TouchGFX工程 李开复口误?大数据时代,我们的隐私真的安全吗? 下一代数据和AI驱动的营销:大数据平台结构化数据占15%左右-可下载 午报 | 大数据杀熟行为下月明令禁止;比亚迪成为苹果新iPad代工方 「全球程序员节」长安计算唐欣:自主计算机整机带动陕西信息产业 通信行业2020年中报总结:Q2整体环比改善,光器件、物联网与IDC表现突出,维持“增持”评级 Best Efforts 1PC 跨库事务 LeetCode-579、查询员工的累计薪水,601、体育馆的人流量 为啥不推荐uuid作为Mysql的主键呢 手把手教学:linux下oracle升级至11.2.0.4 数据库专题——深入理解count(*)为什么这么慢 Linux中MySQL数据库的使用④-----常用查询语句、常用函数 mysql备份与恢复:完全备份,增量备份,基于位置点恢复,基于时间点恢复 MySQL数据库的编译安装过程 《MySQL 入门教程》第 28 篇 字符集与排序规则 PHP面试技巧 之 职场暗语知多少?! [Dubox]实测。还是原来的配方,还是一样的味道! 【剑指金九银十】程序员,如何做到不惑?(附四面拿到字节跳动offer全过程) 超厉害的阿里技术读物,1500页超全计算机系统底层解析宝典 【融中财经早报9.16】大数据杀熟行为10月1日起明令禁止 大数据时代,机器学习算法该如何升级? 怎么判断自己是否适合做程序员 一分钟告诉你什么是区块链 一个时代一个机会,DeFi的起点X-DeFi,如何玩转X-DeFi? RTX3080显卡已被用于挖矿!映众辟谣:工程师测试散热 2020-09-15 比特币相当于房地产,怎样利用比特币获得流动现金? 区块链安全事件与代码审计 fisco bcos solidity销毁合约,删除合约的方法 Linux 中 Golang 的安装和环境配置 Golang interface{} 转换为某个结构体 区块链知识系列 - Raft 共识 OpenHarmony开发者文档开源计划,快快加入吧 融合创新提升服务,大数据搭建信息网 长三角税收一体化按下“快进键” 河钢集团与海尔集团签署物联网生态战略合作协议 1一个拥有万物互联神话,让我们看物联网是如何蓬勃发展的? 重庆大数据产业研究院成立,首批专家服务团名单公布 百分点大数据技术团队:互联网舆情系统的架构实践 水泥大数据研究院郑建辉:四季度水泥价格仍有望冲击去年高位 中国物联网与绿色智慧城市发展论坛召开 欧普照明点亮智慧道路之光 Android & Kotlin:Retrofit + Hilt 实现 看妹子app Android - 控件抖动效果 DevEco studio 一直加载gradle android如何获取调试版及发布版安全码SHA1 Android手机获取IP地址的两种方法 Android Activity 启动过程详解(上) 思维破局:挣钱其实没那么难,关键是方式要对 大数据赋能全产业链 为市民提供“一杯好奶”
您的位置:首页 >开发 >

C语言集锦 | 03 - C语言的复合数据类型(typedef关键字、结构体、枚举体、共用体)

文档版本更新时间更新内容v1.02020-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 intuint16_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:000000000061FDF0st1.name: 000000000061FDF0st1.sex:000000000061FE04st1.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
Mculover666CSDN认证博客专家嵌入式软件开发IoT全栈开发CSDN博客专家,微信公众号mculover666,凭借与生俱来的热爱专注于嵌入式领域,在自己折腾的同时,以文字的方式分享所玩、所思、所想、所悟,作为一个技术人,我们一起前进~

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。