C语言程序内存分配
(1) 内存分区状况
本文引用地址:https://www.eepw.com.cn/article/201611/322296.htm栈区 (stack):
--分配, 释放方式: 由编译器自动分配 和 释放;
--存放内容: 局部变量, 参数;
--特点: 具有 后进先出 特性, 适合用于 保存 回复 现场;
堆区 (heap):
--分配, 释放方式: 由程序员手动 分配(malloc) 和 释放(free), 如果程序员没有释放, 那么程序退出的时候, 会自动释放;
--存放内容: 存放程序运行中 动态分配 内存的数据;
--特点: 大小不固定, 可能会动态的 放大 或 缩小;
堆区内存申请:
--申请过程: OS中有一个记录空闲内存地址的链表, 如果程序员申请内存, 就会找到空间大于申请内存大小的节点, 将该节点从空间内存链表中删除, 并分配该节点;
--剩余内存处理: 系统会将多余的部分重新放回 空闲内存链表中;
--首地址记录大小: 分配内存的首地址存放该堆的大小, 这样释放内存的时候才能正确执行;
全局区/静态区 (数据段 data segment /bss segment):
--分配, 释放方式: 编译器分配内存, 程序退出时系统自动释放内存;
--存放内容: 全局变量, 静态变量;
--特点: 全局变量 和 静态变量存储在一个区域, 初始化的两种变量 和 未初始化的 存储在不同区域, 但是两个区域是相邻的;
常量区:
--分配, 释放方式: 退出程序由系统自动释放;
--存放内容: 常量;
代码区 (text segment):
--分配, 释放方式: 编译器分配内存, 程序退出时系统自动释放内存;
--存放内容: 存放 程序的二进制代码, 和一些特殊常量;
内存存放顺序 (由上到下): 栈区 -> 堆区 -> 全局区 -> 常量区 -> 代码区;
(2) 内存分配方式
全局内存分配:
--生命周期: 编译时分配内存, 程序退出后释放内存, 与 程序 的生命周期相同;
--存储内容: 全局变量, 静态变量;
栈内存分配:
--生命周期: 函数执行时分配内存, 执行结束后释放内存;
--特点: 该分配运算由处理器处理, 效率高, 但是栈内存控件有限;
堆内存分配:
--生命周期: 调用 malloc()开始分配, 调用 free()释放内存, 完全由程序员控制;
--谨慎使用: 如果分配了 没有释放, 会造成内存泄露, 如果频繁 分配 释放 会出现内存碎片;
(3) register变量
使用场景: 如果 一个变量使用频率特别高, 可以将这个变量放在 CPU 的寄存器中;
--修饰限制: 只有 局部变量 和 参数 可以被声明为 register变量, 全局 和 静态的不可以;
--数量限制: CPU 寄存器 很宝贵, 不能定义太多register变量;
(4) extern 变量
extern变量概念: 声明外部变量, 外部变量就是在函数的外部定义的变量, 在本函数中使用;
--作用域: 从外部变量定义的位置开始, 知道本源码结束都可以使用, 但是只能在定义extern后面使用, 前面的代码不能使用;
--存放位置: 外部变量 存放在 全局区;
extern变量作用: 使用extern修饰外部变量, ① 扩展外部变量在本文件中的作用域, ② 将外部变量作用域从一个文件中扩展到工程中的其它文件;
extern声明外部变量的情况:
--单个文件内声明: 如果不定义在文件开头, 其作用范围只能是 定义位置开始, 文件结束位置结束;
--多个文件中声明: 两个文件中用到一个外部变量, 只能定义一次, 编译 和 连接的时候, 如果没有这个外部变量, 系统会知道这个外部变量在别处定义, 将另一个文件中的外部变量扩展到本文件中;
extern编译原则:
--本文件中能找到: 编译器遇到 extern 的时候, 现在本文件中找外部变量的定义的位置, 如果找到, 就将作用域扩展到 定义的位置 知道文件结束;
--本文件中找不到: 如果本文件中找不到, 连接其它文件找外部变量定义, 如果找到, 将外部变量作用域扩展到本文件中;
--外部文件找不到: 报错;
使用效果: extern 使用的时候, 可以不带数据类型;
--本文件: int A = 0; 在第10行, extern A 在第一行, 那么A的作用域就扩展为从第一行到文件末尾;
--多文件: 在任意文件中定义了 int A = 0; 在本文件中声明 extern A, 那么从当前位置到文件末尾都可以使用该变量;
(5) static变量 与 全局变量区别
static 变量 与 全局变量 相同点: 全局变量是静态存储的, 存储的方式 和 位置基本相同;
static 变量 与 全局变量不用点: 全局变量的作用域是 整个项目工程 横跨过个文件, 静态变量的作用域是 当前文件, 其它文件中使用是无效的;
变量存储位置: 全局变量 和 静态变量 存放在 全局区/静态去, 局部变量存放在 栈区(普通变量) 和 堆区(指针变量);
变量静态化:
--局部变量: 局部变量 加上 static , 相当于将局部变量的生命周期扩大到了整个文件, 作用域不改变;
--全局变量: 全局变量 加上 static , 相当于将全局变量的作用域缩小到了单个文件, 生命周期是整个程序的周期;
关于函数头文件的引申:
--内部函数: 单个文件中使用的内部函数, 仅在那个特定文件中定义函数即可;
--全局函数: 如果要在整个工程中使用一个全局函数, 需要将这个函数定义在一个头文件中;
static变量与普通变量区别:
--static全局变量 与 全局变量区别: static 全局变量 只初始化一次, 防止在其它文件中使用;
--static局部变量 与 局部变量区别: static 局部变量 只初始化一次, 下一次依据上一次结果;
static函数与普通函数区别: static 函数在内存中只保留一份, 普通函数 每调用一次, 就创建一个副本;
.
(6) 堆 和 栈比较
堆(heap)和栈(stack)区别:
--申请方式: stack 由系统自动分配, heap 由程序员进行分配;
--申请响应: 如果 stack 没有足够的剩余空间, 就会溢出; 堆内存从链表中找空闲内存;
--内存限制: stack 内存是连续的, 从高位向低位扩展, 而且很小, 只有几M, 是事先定好的, 在文件中配置; heap 是不连续的, 从低位向高位扩展, 系统是由链表控制空闲程序, 链表从低地址到高地址, 堆大小受虚拟内存限制, 一般32位机器有4G heap;
--申请效率: stack 由系统分配, 效率高; heap 由程序员分配, 速度慢, 容易产生碎片;
(7) 各区分布情况
.
按照下图分布: 由上到下顺序 : 栈区(stack) -> 堆区(heap) -> 全局区 -> 字符常量区 -> 代码区;
验证分区状况:
--示例程序:
- /*************************************************************************
- >FileName:memory.c
- >Author:octopus
- >Mail:octopus_work.163.com
- >CreatedTime:Mon10Mar201408:34:12PMCST
- ************************************************************************/
- #include
- #include
- intglobal1=0,global2=0,global3=0;
- voidfunction(void)
- {
- intlocal4=0,local5=0,local6=0;
- staticintstatic4=0,static5=0,static6=0;
- int*p2=(int*)malloc(sizeof(int));
- printf("子函数局部变量:");
- printf("local4:%p",&local4);
- printf("local5:%p",&local5);
- printf("local6:%p",&local6);
- printf("子函数指针变量:");
- printf("p2:%p",p2);
- printf("全局变量:");
- printf("global1:%p",&global1);
- printf("global2:%p",&global2);
- printf("global3:%p",&global3);
- printf("子函数静态变量:");
- printf("static4:%p",&static4);
- printf("static5:%p",&static5);
- printf("static6:%p",&static6);
- printf("子函数地址:");
- printf("function:%p",function);
- }
- intmain(intargc,char**argv)
- {
- intlocal1=0,local2=0,local3=0;
- staticintstatic1=0,static2=0,static3=0;
- int*p1=(int*)malloc(sizeof(int));
- constintconst1=0;
- char*char_p="char";
- printf("主函数局部变量:");
- printf("local1:%p",&local1);
- printf("local2:%p",&local2);
- printf("local3:%p",&local3);
- printf("const1:%p",&const1);
- printf("主函数指针变量:");
- printf("p1:%p",p1);
- printf("全局变量:");
- printf("global1:%p",&global1);
- printf("global2:%p",&global2);
- printf("global3:%p",&global3);
- printf("主函数静态变量:");
- printf("static1:%p",&static1);
- printf("static2:%p",&static2);
- printf("static3:%p",&static3);
- printf("字符串常量:");
- printf("char_p:%p",char_p);
- printf("主函数地址:");
- printf("main:%p",main);
- printf("===============");
- function();
- return0;
- }
--执行结果:
- [root@ip28pointer]#gccmemory.c
- [root@ip28pointer]#./a.out
- 主函数局部变量:
- local1:0x7fff75f5eedc
- local2:0x7fff75f5eed8
- local3:0x7fff75f5eed4
- const1:0x7fff75f5eed0
- 主函数指针变量:
- p1:0x19bad010
- 全局变量:
- global1:0x600e14
- global2:0x600e18
- global3:0x600e1c
- 主函数静态变量:
- static1:0x600e34
- static2:0x600e30
- static3:0x600e2c
- 字符串常量:
- char_p:0x4009f7
- 主函数地址:
- main:0x40065f
- ===============
- 子函数局部变量:
- local4:0x7fff75f5eea4
- local5:0x7fff75f5eea0
- local6:0x7fff75f5ee9c
- 子函数指针变量:
- p2:0x19bad030
- 全局变量:
- global1:0x600e14
- global2:0x600e18
- global3:0x600e1c
- 子函数静态变量:
- static4:0x600e28
- static5:0x600e24
- static6:0x600e20
- 子函数地址:
- function:0x400528
3. 指针与地址
评论