内存布局

memory.png 程序的内存布局分为不同段,从低地址到高地址为:
- 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放入即可

image.png
**stack有静态和动态分配两种方式;而heap只有动态分配

静态变量

  • 局部静态变量: 作用域不变,生命周期延长
  • 全局静态变量: 作用域缩小,生命周期不变 Ch6 内存布局和分配 - 图3

    字符串常量

    1. #include <stdio.h>
    2. void main(void) {
    3. char s[] = "cat";
    4. const char *p = "rat";
    5. printf("%s chases %s\n", s, p);
    6. s[0] = 'C';
    7. char* sp = s;
    8. *(sp + 1) = 'A';
    9. printf("%s chases %s\n", s, p);
    10. //最终输出:
    11. //cat chases rat
    12. //CAt chases rat
    13. }

    C中对于字符串初始化通常有两种方式(不考虑string.h)

  • char s[] = "..."

  • char *p = "..."(C11后规定只能const char*s = "...")

image.png
前者易理解,对于后者:相当于将一个字符内容为 “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))

静态分配的缺点

  1. 命名可能产生冲突
  2. 不灵活,因为是在编译前就分配了空间,程序运行中无法调整空间大小
  3. 生命周期直到程序结束,因此对于暂时使用的变量产生空间浪费
  4. 无法实现递归,因为递归本身是动态过程,随着递归深入,当前帧必须进行”入栈-出栈”

动态分配

定义:程序在执行时进行内存分配,heap只能动态分配,stack也能进行动态分配

stack分配

  • 变长数组即为栈上动态分配的代表,使用alloca向stack申请空间

    heap分配

  • 堆是由malloc()函数(C++ new)分配的内存块(chunk),内存释放由程序员手动控制,在C语言为free函数完成(C++ delete)

    对比

    | | stack分配 | heap分配 | | :—-: | :—-: | :—-: | | 管理方式 | 全自动,由compiler按需分配/清除 | 程序员手动使用malloc()/free()
    内存泄漏:没能free分配的heap空间 | | 空间大小 | 栈向低地址连续生长,栈顶已经被限制,最大容量有限,故一般栈空间较小 | 堆向高地址生长,可不连续,可获得空间更大 | | 碎片产生 | 栈时连续内存空间,没有碎片 | 堆不连续,容易产生碎片 | | 增长方向 | 低地址 | 高地址 | | 分配效率 | 高,栈是机器系统提供的数据结构,计算机底层硬件支持 | 低,堆需要依靠算法进行管理 |


选择题知识点

  1. 当某个compiler静态存储所有的变量,返回地址,寄存器等,则理论上①局部变量②函数调用③递归中的①②可以继续实现:在数据段保存变量,地址即可
  2. ⭐阻碍C语言自动释放heap空间的原因是(即自动free)

①指针不一定都被初始化:未考虑该问题的编译器可能为未初始化指针随机分配地址,一旦随机到被占用的地址,随意free会产生问题
②强转(casting)使得指针身份无法确定:free的时候,依靠的只有malloc时的地址和分配内存的大小—>当指针被强转其他类型,对应区域将无法被free

  1. 要在堆上分配100个long的空间是,语句是 long* a = (long*)malloc(100*sizeof(long))
  2. void* malloc()返回值
    • 分配成功:返回分配空间的地址/指针
    • 分配失败; 返回空指针NULL