内存管理和静态区

内存结构和静态区域

内存结构

内存结构

图中以32位系统为例,且从下往上地址逐渐变大

在32位操作系统中,最多可以寻址2^32bytes ≈ 4G 的虚拟内存空间

虚拟内存空间

虚拟内存空间,通常被分为用户空间内核空间两部分。

  • 用户空间:应用和进程。
  • 内核空间:操作系统内核和关键的系统功能。

隔离原因

  1. 用于隔离应用和操作系统,避免访问或破环内核空间,有一定保护作用。

  2. 便于权限管理。通常来说,当进程运行在内核空间时就处于内核态,而进程运行在用户空间时则处于用户态

  3. 当处于用户态的一个进程想要执行高权限操作时,通常就要使用系统调用

内核空间

是操作系统内核代码和数据所占用的内存区域。

  1. 用户代码不能读取也不能写入这些地址,如果尝试这样做,会导致分段错误(Segmentation Fault)。

  2. 内核空间通常位于内存的高地址部分,而用户空间位于内存的低地址部分。

  3. 内核空间通常只能由内核代码访问,用户态程序需要通过系统调用(System Calls)与内核进行交互

用户空间

  1. 存储程序运行时所需的局部变量和函数调用信息
  2. 栈区通常从高地址向低地址分配内存。与作用域和函数调用的生命周期相关。

  1. 存储程序运行时动态分配的内存
  2. 多数情况下,地址增长方向和栈相反
  3. 堆区的内存分配和释放由程序员通过调用内存管理函数(如malloccallocreallocfree等)来手动进行。

内存映射段

  1. 其中文件被映射到进程的虚拟内存空间,以减少I/O开销并简化文件访问。

BSS 段

  1. 存储未初始化的全局变量和静态变量。

数据段

  1. 已初始化数据段:存储已经初始化的全局变量和静态变量。

  2. 常量区——只读属性:

  • 字符串,具有常性
  • const限定的全局变量:使用const关键字声明的全局或静态变量,它们的值在编译时就被确定,不能在运行时修改。
  • 编译时确定的常量表达式:例如,可以使用#define预处理指令或者const关键字定义的常量表达式。

代码段

  1. 也称为文本段,在内存中的主要作用是存储程序的可执行代码

静态区总结

它们在程序的整个生命周期内都存在

BSS段

  • 未初始化的静态变量和全局变量。

数据段

  • 已初始化的静态变量和全局变量

局部静态变量

  • 静态局部变量在程序运行期间保持其值。
  • 静态局部变量在整个程序执行期间都存在无法再被初始化(经典的场景有在循环中嵌套一个局部静态变量)。

类中的静态变量

实验部分

验证堆栈的内存分配走向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <stdlib.h>

void test_function() {
int stack_var;
printf("在main之后被调用的栈中的变量地址: %p\n", &stack_var);
}

int main() {
int stack_var1;
int* heap_var1 = (int*)malloc(sizeof(int));

printf("main中的变量地址: %p\n", &stack_var1);
printf("malloc开辟的第一个空间: %p\n", heap_var1);

test_function();

int* heap_var2 = (int*)malloc(sizeof(int));


printf("malloc开辟的第二个空间 %p\n", heap_var2);

free(heap_var1);
free(heap_var2);

return 0;
}

运行结果

upload successful

  • 高地址向低地址分配内存,且堆的分配方向和它相反。
  • 因为这样可以在栈顶和堆之间留出足够的可用内存空间。当函数返回时,相应的栈帧会被弹出,释放分配的内存。这种分配方式使得栈能够高效地管理内存。

内存管理和静态区
https://weihehe.top/2024/07/05/C-内存对齐策略/
作者
weihehe
发布于
2024年7月5日
许可协议