内存布局
程序的内存布局分为不同段,从低地址到高地址为: - text segment/代码区:存放的是程序的全部代码(机器指令),来源于二进制.exe中的代码部分 - initialized data seg/数据段:包含了已初始化全局变量和已初始化的static局部变量 - Uninitialized data seg/bss区:包含未初始化全局变量和未初始化static局部变量,C规定变量初始值都为0,因此bss中存放的都为0 - Stack(栈):记录自动变量以及每次函数调用时所需信息(存放活动记录),属于动态/静态**分配 - Heap(堆):通常在堆中进行动态**分配,由于历史上形成的惯例,堆位于非初始化数据段顶和栈底间 |
|
---|---|
静态分配
参考链接: C语言中内存分配
定义: **compiler在处理程序源代码时分配内存空间,在程序执行之前进行,效率比较高**
stack分配
下图代码中int n=1
- 变量a在编译阶段已分配在stack中[ebp-8]处空间,执行时将1放入即可
**stack有静态和动态分配两种方式;而heap只有动态分配
静态变量
- 局部静态变量: 作用域不变,生命周期延长
-
字符串常量
#include <stdio.h>
void main(void) {
char s[] = "cat";
const char *p = "rat";
printf("%s chases %s\n", s, p);
s[0] = 'C';
char* sp = s;
*(sp + 1) = 'A';
printf("%s chases %s\n", s, p);
//最终输出:
//cat chases rat
//CAt chases rat
}
C中对于字符串初始化通常有两种方式(不考虑string.h)
char s[] = "..."
char *p = "..."(C11后规定只能const char*s = "...")
前者易理解,对于后者:相当于将一个字符内容为 “rat\0” 的字符数组首地址赋值给字符指针p,不允许使用字符指针修改字符串中数值即*p = 'R'
是不被允许的,因此C11后强制要求使用const char 初始化字符串,表示指针指向数据对指针来说是不可变的([const char p等价于char const p,区别于char const p](https://blog.csdn.net/qq_40244176/article/details/80765975))
静态分配的缺点
- 命名可能产生冲突
- 不灵活,因为是在编译前就分配了空间,程序运行中无法调整空间大小
- 生命周期直到程序结束,因此对于暂时使用的变量产生空间浪费
- 无法实现递归,因为递归本身是动态过程,随着递归深入,当前帧必须进行”入栈-出栈”
动态分配
定义:程序在执行时进行内存分配,heap只能动态分配,stack也能进行动态分配
stack分配
变长数组即为栈上动态分配的代表,使用alloca向stack申请空间
heap分配
堆是由malloc()函数(C++ new)分配的内存块(chunk),内存释放由程序员手动控制,在C语言为free函数完成(C++ delete)
对比
| | stack分配 | heap分配 | | :—-: | :—-: | :—-: | | 管理方式 | 全自动,由compiler按需分配/清除 | 程序员手动使用malloc()/free()
内存泄漏:没能free分配的heap空间 | | 空间大小 | 栈向低地址连续生长,栈顶已经被限制,最大容量有限,故一般栈空间较小 | 堆向高地址生长,可不连续,可获得空间更大 | | 碎片产生 | 栈时连续内存空间,没有碎片 | 堆不连续,容易产生碎片 | | 增长方向 | 低地址 | 高地址 | | 分配效率 | 高,栈是机器系统提供的数据结构,计算机底层硬件支持 | 低,堆需要依靠算法进行管理 |
选择题知识点
- 当某个compiler静态存储所有的变量,返回地址,寄存器等,则理论上①局部变量②函数调用③递归中的①②可以继续实现:在数据段保存变量,地址即可
- ⭐阻碍C语言自动释放heap空间的原因是(即自动free)
①指针不一定都被初始化:未考虑该问题的编译器可能为未初始化指针随机分配地址,一旦随机到被占用的地址,随意free会产生问题
②强转(casting)使得指针身份无法确定:free的时候,依靠的只有malloc时的地址和分配内存的大小—>当指针被强转其他类型,对应区域将无法被free
- ⭐要在堆上分配100个long的空间是,语句是
long* a = (long*)malloc(100*sizeof(long))
- ⭐void* malloc()返回值
- 分配成功:返回分配空间的地址/指针
- 分配失败; 返回空指针NULL