Cover midjourney prompt:Puppies and girls. anime style.
名词下方的代码框中,会有更加深入的解释。
内核空间(Kernel space)
:是操作系统内核代码和数据所占用的内存区域。
①用户代码不能读取也不能写入这些地址,如果尝试这样做,会导致分段错误(Segmentation Fault)
②内核空间通常位于内存的高地址部分,而用户空间位于内存的低地址部分。
③内核空间通常只能由内核代码访问,用户态程序需要通过系统调用(System Calls)与内核进行交互。
④在虚拟内存空间中,内核空间和用户空间被划分为两个独立的部分。尽管每个进程都有其自己的虚拟内存空间,但内核空间通常在所有进程的虚拟内存空间中保持相同。这意味着每个进程都可以访问相同的内核空间(当然,受到相应的访问控制限制),从而允许操作系统内核在所有进程间共享代码和数据。
用户空间(User space)
栈(Stack,(地址向下增长):存储程序运行时所需的局部变量和函数调用信息。
命令行参数区:在C语言程序中,命令行参数通过`main`函数的两个参数`int argc`和`char *argv[]`来传递。其中,`argc`表示传递给程序的参数个数(包括程序名称在内),`argv`是一个指向字符指针数组的指针,它包含了每个参数的地址。
栈区通常从高地址向低地址分配内存。当一个函数被调用时,它的栈帧被压入栈中,分配内存空间以存储局部变量、函数参数等。当函数返回时,其栈帧被弹出,释放相应的内存空间。栈区的内存分配和释放是自动进行的,与作用域和函数调用的生命周期相关。
内存映射段(Memory Mapping Segment):文件映射(包括动态库)和匿名映射。
它的主要用途是将文件或设备映射到进程的内存地址空间,从而允许程序直接访问这些资源,而不需要额外的文件或设备I/O操作。
堆(Heap,(多数情况下,地址增长方向和栈相反):存储程序运行时动态分配的内存。
堆区通常从低地址向高地址分配内存。堆区的内存分配和释放由程序员通过调用内存管理函数(如`malloc`,`calloc`,`realloc`和`free`等)来手动进行。由于堆区的内存分配是动态的,需要确保正确地分配和释放内存以避免内存泄漏或其他错误。
BSS 段:存储未初始化的全局变量和静态变量。
未初始化数据段(Uninitialized Data Segment):存储未初始化的全局变量和静态变量。这部分内存通常被称为BSS段(Block Started by Symbol)。
数据段(Data segment):数据段用于存储全局变量和静态变量。。
1.已初始化数据段(Initialized Data Segment):存储已经初始化的全局变量和静态变量。
2.常量区
字符串字面值(String literals):字符串字面值(例如:"Hello, World!")通常存储在只读数据段(Read-Only Data segment)中。这意味着字符串字面值在程序运行期间是不可修改的。
②`const`限定的全局变量:使用`const`关键字声明的全局或静态变量,它们的值在编译时就被确定,不能在运行时修改。
③编译时确定的常量表达式:例如,可以使用`#define`预处理指令或者`const`关键字定义的常量表达式。
代码段(Code segment),也称为文本段(Text segment),在内存中的主要作用是存储程序的可执行代码。
解答
a.什么叫“向下增长?”
答:当我们创建栈帧的时候,我们会优先使用“高地址”,当又需要创建栈帧的时候,从高地址往下申请地址使用。
一个例子
#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("第一个堆: %p\n", heap_var1);
test_function();
int* heap_var2 = (int*)malloc(sizeof(int));
printf("第二个堆 %p\n", heap_var2);
free(heap_var1);
free(heap_var2);
return 0;
}
说明:这个程序展示了栈区和堆区内存分配的示例。通过观察stack_var1和stack_var的地址(分别位于main函数和test_function函数的栈帧中),可以看到栈区的地址是从高到低分配的。同时,观察heap_var1和heap_var2的地址(分别在堆区中分配),可以看到堆区的地址是从低到高分配的。
输出:
b.为什么多数情况下,栈和堆的地址增长方式相反?
答:栈向低地址方向增长是因为这样可以在栈顶和堆之间留出足够的可用内存空间。当函数返回时,相应的栈帧会被弹出,释放分配的内存。这种分配方式使得栈能够高效地管理内存。
在C语言中,静态区域(Static Area)的数据在程序加载时分配,并在程序运行期间保持不变。它们在程序的整个生命周期内都存在。
包括以下成员:静态区主要包含以下部分:
1.BSS段(未初始化的静态变量和全局变量)。
2.数据段(已初始化的静态变量和全局变量)。
3.局部静态变量(注意作用域和可见性),静态局部变量在程序运行期间保持其值,而不是在每次函数调用时创建和销毁。这意味着静态局部变量在整个程序执行期间都存在。无法 **再** 被初始化(经典的场景有在循环中嵌套一个局部静态变量)。