text代码段:二进制指令、常量存储的地方
data数据段:初始化的全局变量、静态局部变量存储的地方
bbs静态数据段:未初始化的全局变量、局部静态变量存储的位置,在该段的数据,程序开始之前就会清理为0;
stack栈内存:局部变量、块变量存储位置,由操作系统创建、释放,使用方便、安全性高,但内存小,释放时间不可控
heap堆:由程序员自己创建释放的内存块,内存大,使用麻烦,容易疏忽造成内存泄漏等情况
为什么要使用堆内存:随着程序的复杂数据变多,其他内存段的申请和释放不受控制,堆内存的申请释放受控制
内存泄漏:每次申请内存并使用后没有释放,日积月累下就会造成能使用的内存越来越少,这就是内存泄漏。
判断是否有内存泄漏:1.查看内存使用情况
2。借用代码分析工具mtrace,检查调用malloc free的使用情况
3.封装新的malloc和free函数,记录调用信息到日志中
内存碎片:已经释放但是又无法继续使用的内存叫做内存碎片,是由于申请和释放的时间不协调导致的,内存碎片无法避免只能尽量减少
如何减少内存碎片:
1、尽量使用栈内存,栈内存不会产生内存碎片
2、不要频繁地申请和释放内存
3、尽量申请大块内存自己管理
auto:用于定义自动申请、自动释放的量(局部变量),不加就代表加了
注意:不用用它修饰全局变量
extern:用于声明外部变量,意思是告诉编译器此变量在程序的其他地方已经定义了,先让程序通过编译,如果在链接时找不到该变量依然会报错,不建议在extern时赋值,它只是声明
static的作用:
1.改变存储位置:改变局部变量的存储位置,由stack改为data或bss
2.延长生命周期:延长局部变量的使用范围,直到程序结束才释放
3.限制全局变量的作用域:使用static修饰全局变量,可以防止该变量被别的文件使用、
const的作用:
保护变量的值不被显式修改,但是可以通过内存进行修改,使用const修饰data段数据,那么该数据会存储到text段中,如果强制修改会发生段错误。
为什么使用指针
1.函数之间内存是相互独立的,但是有时候需要共享
2.函数之间的值传递是单向的,当传递的字节数变多时,效率较低,使用指针传递他们的地址,4位或8位,能提高效率
3.堆内存无法使用名字,只能使用指针变量指向堆内存的地址从而使用堆内存
空指针:值为NULL的指针;
对空指针解引用一定段错误,用于初始化以及函数返回值的错误标志
如何避免空指针带来的错误:
来历不明的指针使用前先判断函数的返回值、函数的参数
野指针:指针的值不确定
对野指针解引用的结果:1.一切正常 2.段错误 3.脏数据
如何不产生野指针:
1、指针一定要初始化
2、不要返回局部变量的地址
3、堆内存释放后,指向堆内存的指针及时置空
volatile: 让变量的取值取消取值优化过程,无论该变量的值是否显式改变,每次获取都去内存取值,在硬件编程、多线程编程中常用
register: 申请变量存储到寄存器中,加快访问速度,寄存器数量有限,不一定成功,而且修饰的变量无法取地址
typedef作用:
类型重定义,在定义变量前,加入typedef,那么原本的变量名就变成这种数据类型了,可以像数据类型一样定义变量
存在内存对齐和内存补齐,目的是为了提高内存的访问速度
内存对齐:假设第一个成员从0地址开始,每个成员所使用的地址编号必须是它类型的整数倍,如果不是,则填充空白字节直到是为止(数组按成员类型字节算)
内存补齐:结构体的总字节数必须是它最大成员字节数的整数倍,如果不是,则在某尾填充空白字节。
大小端判断:
小端:高位地址存储高位数据
大端:高位地址存储低位数据
注意:一般的个人PC都是小端系统,UNIX服务器和网络设备都是大端系统
枚举常量和宏定义的区别:
1、枚举常量需要占用内存,而定义宏常量不占用内存
2、枚举常量的设计目的是为了限制实际数据输入、定义宏是为了完成代码的替换和维护
3、枚举常量是具有类型,宏定义没有类型
全局变量:定义在函数外的变量
存储位置:data(初始化)或者(未初始化)
生命周期:程序开始到程序结束
使用范围:程序的任意位置都可以使用
局部变量:定义在函数内的变量
存储位置:stack栈内存
生命周期:从函数开始到函数结束
使用范围:只能在该函数内使用
块变量: 定义在if/for/while等语句块内的变量
存储位置:stack栈内存
生命周期:从语句块开始到语句块结束
使用范围:只能在语句内使用
注意:同名的局部变量会屏蔽同名的全局变量,同名的块变量会屏蔽同名的全局、局部变量,因此建议全局变量首字母大写、局部变量全部小写