第 13 章 自定义数据类型
12.3 共用体
(1)结构体变量所占内存长度,可以认为是各成员占的内存长度的叠加;每个成员分别占有其自己的内存单元。
(2)共用体变量所占的内存长度等于最长的成员的长度;几个成员共用一个内存区。
① 定义共同体类型
union 类型名
{
类型 成员名称;
类型 成员名称;
类型 成员名称;
...
}
注:类型名称和成员名称需要满足标识符规范!
② 共用体变量
1. 定义共同体变量
① 先定义共用体类型,再定义共用体变量
② 同时定义共用体类型和共用体变量
③ 同时定义共用体类型和枚举变量,并省略共用体类型的名称
2. 共用体变量的初始化
① 先声明变量,再给成员初始化赋值
② 同时声明变量并给第一个成员初始化赋值
union 类型 变量名 = {第一个成员的值}
② 同时声明变量并给指定成员初始化赋值
union 类型 变量名 = {.成员名称 = 第一个成员的值}
3. 访问共用体变量的成员
. 运算符
③ 共用体指针
指针访问成员使用 -> 或者 (*指针).成员名称
④ 共用体存储大小(内存)
共同体的存储大小是 最大成员的长度
13.4 typedef
① 基本类型别名
语法结构:
typedef 类型名 别名;
——结构体 与 共用体 别名语法相同,单纯两个的意义不同
② 结构体别名
第一种写法:先定义结构体类型,再取别名
第二种写法:同时定义结构体类型并取别名
第三种写法:同时定义结构体类型并取别名,且省略结构体类型名称 ——推荐
③ 共用体别名
第一种写法:先定义共用体类型,再取别名
第二种写法:同时定义共用体类型并取别名
第三种写法:同时定义共用体类型并取别名,且省略共用体类型名称
——数组、指针别名 都是把之前(数组/函数名)变量的名称 替换为 别名
④ 数组别名
元素类型名 别名[数组长度]
⑤ 指针别名
指向类型名 *别名;
第14章 动态内存分配
① C 语言内存模型
栈区(Stack): 局部变量
堆区(Heap):动态分配的内存空间
静态区:全局变量、静态局部变量
代码区:字面量常量、函数代码块
② void *指针
1. void * 类型的指针可以指向任何类型的数据
2. void * 类型的值【不能解引用】
3. 任何类型的指针都可以转为 void *类型的指针 (一般不需加强制转换,不会有警告)
void *类型的指针可以转为任何类型的指针(建议【加上强制类型转换】) —— void *转为其他类型指针
③ 动态内存分配函数
——以下函数来自于标准库头文件<stdlib.h>
malloc() 分配【指定字节长度】的内存空间
原型:void *malloc(size_t size);
calloc() 分配内存空间,指定【元素个数和单个元素存储长度】
原型:void *calloc(size_t num_elements, size_t element_size);
realloc() 调整已分配内存空间的大小
原型:void *realloc(void *ptr, size_t size);
——// realloc 返回新的空间的地址,该地址尽量与原空间一致,但不保证
free() 释放分配的内存空间
④ 动态内存分配基本原则
(1)避免分配大量的小内存块。分配堆上的内存有(一部分是)一些系统开销,所以分配许多小的内存块比分配几个大内存块的系统开销大。
(2)仅在需要时分配内存。只要使用完堆上的内存块,就需要及时释放它,否则可能出现【内存泄漏】。
(3)总是确保释放已分配的内存。在编写分配内存的代码时,就要确定好在代码的什么地方释放内存。
⑤ 内存泄漏和内存溢出
内存泄漏: 内存空间没有被正确释放,称为内存泄漏
内存溢出: 当系统分配内存空间的时候发现不够用了,称为内存溢出; 内存泄漏增加内存溢出的风险。
第 15 章 预处理器
15.1 基本介绍(不用记)
预处理器:
预处理器的主要任务包括 宏替换、文件包含、条件编译等。
预处理指令:
(1)预处理指令应该放在代码的开头部分。
(2)预处理指令都以 # 开头,指令前面可以有空白字符(比如空格或制表符),# 和指令的其余部分之间也可以有空格,但是为了兼容老的编译器,一般不留空格。
(3)预处理指令都是一行的,除非在行尾使用反斜杠,将其折行。
(4)预处理指令不需要分号作为结束符,指令结束是通过【换行符】来识别的;如果写分号,分号会成为预处理指令的一部分。
(5)预处理指令【通常不能写在函数内部】,有些编译器的扩展允许将预处理指令写在函数里,但强烈不建议这么干。
15.2 宏定义 #define
① 宏定义
—— 用于文本替换
#define
1. 使用宏定义定义【常量】
2. 使用宏定义给【数据类型】取别名(建议使用typedef)
3. 【表达式和语句】也可以作为宏定义的替换文本
4. 替换文本中可以包含【其他宏名称】
5. 可以使用 #undef 取消宏定义 ——只要没执行取消,前面都可以使用
② 取消宏定义
#undef
③ 带参数的宏定义
与函数区别:
(1)宏展开仅仅是【文本的替换】,不会对表达式进行计算;宏在编译之前就被处理掉了,它没有机会参与编译,也不会占用内存。
(2)函数是一段可以重复使用的代码,会被编译,会给它分配内存,每次调用函数,就是执行这块内存中的代码。
eg: // 带参数的宏定义 返回两个数中较大的
#define MAX(x,y) x > y ? x : y
int main()
{
int num = MAX(160, 45); // MAX(160, 45) 替换为 160 > 45 ? 160 : 45;
return 0;
}
15.3 文件包含 #include
① 包含标准库头文件
#include <头文件名称>
② 包含自定义头文件
#include "自定义头文件路径"
1. 相对路径
从当前文件开始,找目标文件
./ 表示当前目录(当前文件所在的目录)
./ 开头的路径可以省略 ./
../ 表示上一级目录
../../ 表示上上级目录
../../../ 表示上上上级
2. 绝对路径
windows系统,以盘符开头,路径分隔符默认是 \, / 也可以使用
linux 系统,以 / 开头,路径分隔符只能是 /
总结
-
动态内存分配
1.1 C语言内存模型:栈区、堆区、静态区、代码区
1.2 void 类型指针
1.3 动态内存分配的函数:malloc()、calloc()、realloc()、free()
1.4 内存泄漏、内存溢出 -
预处理器
- 宏定义 #define #undef
- 文件包含 #include
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)