petard  

第 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.1 C语言内存模型:栈区、堆区、静态区、代码区
    1.2 void 类型指针
    1.3 动态内存分配的函数:malloc()、calloc()、realloc()、free()
    1.4 内存泄漏、内存溢出

  2. 预处理器

    1. 宏定义 #define #undef
    2. 文件包含 #include
posted on 2024-04-16 00:02  岌岌无名  阅读(8)  评论(0编辑  收藏  举报