一、内存的概念
C语言内存的概念是指在C编程语言中,计算机用于存储数据和程序的空间。内存在C语言中是一个重要的概念,程序员需要了解如何分配、使用和释放内存,以确保程序的正确性和性能。
(CPU负责加减乘除的运算)
二、内存分配
分区示意图
堆从高地址向低地址分配,而栈从低地址向高地址分配。
低地址到高地址:内存中的数据和程序是按照递增的方式存储。
(1)栈区(Stack)
主要用于存储局部变量和调用函数
(a)用途
存储函数调用信息:每当函数被调用时,会创建一个新的栈帧(stack frame)或称为活动记录(activation record),用于存储函数的参数、局部变量、返回地址以及其他相关信息。当函数返回时,栈帧被销毁,函数的控制返回到调用它的位置。
(栈帧里面通常存放了(参数,返回地址,局部变量))
栈的操作:
- 当函数被调用时,计算机将创建一个新的栈帧,并将其推入栈的顶部(通常位于栈的高地址端)。
- 新的栈帧会成为当前活动栈帧,它包含了该函数的所有信息,如参数、返回地址和局部变量。
- 函数内部执行过程中,可以在当前栈帧中访问和操作这些数据。
- 当函数执行完毕并准备返回时,计算机会弹出当前栈帧,将控制权返回给调用函数。这将销毁当前栈帧,并将执行位置恢复到调用函数的返回地址。
(b)分配方式
- 栈内存的分配和释放是自动的,无需程序员干预。栈帧的创建和销毁由编译器和运行时系统负责。
- 栈内存中的分配和释放速度通常非常快,因为栈上的数据是连续存储的,不需要复杂的内存管理。
(c)大小限制
- 栈内存通常具有固定的大小限制,这取决于操作系统和编译器的配置。栈的大小通常比堆内存小得多。
- 如果在栈上分配过多的内存或者递归调用层数过多,可能会导致栈溢出(Stack Overflow)错误,这会导致程序异常终止。
示例代码
void b(){ .... } void a(){ b(); .... }
当调用a时,a会入栈,a调用b入栈,当执行完毕,b先弹栈,a在弹
(2)堆区(Heap)
(a)用途
堆内存允许程序在运行时动态地分配内存空间,而不需要在编译时预先知道数据的大小。这对于存储可变大小的数据结构非常有用,如动态数组、链表、树等。
(b)分配方式
堆内存的分配和释放需要由程序员手动管理。C语言提供了一些标准库函数来进行堆内存的分配和释放,包括:
- malloc(size_t size):分配指定大小的内存块,并返回指向该内存块的指针。
- calloc(size_t num, size_t size):分配指定数量和大小的内存块,并返回指向该内存块的指针。分配的内存会被初始化为零。
- realloc(void *ptr, size_t size):重新分配已分配内存的大小,并返回指向重新分配后内存块的指针。
- free(void *ptr):释放之前分配的堆内存,以便将其返回给操作系统。
(在C++中new[]也是存放在堆内存中)
(c)大小限制
堆内存的大小通常比栈内存大得多,因此可以用于存储大型数据结构或大容量数据,因此堆的大小不受限制
示例代码:
int *array = malloc(size * (sizeof(int))); //创建一个size大小的int地址 //释放 free(array);
(3)全局(静态)区(Data)
(a)用途
用于存储全局变量和静态变量的内存区域。全局变量和静态变量是在程序的整个生命周期内存在的,它们的内存分配和释放是在程序启动和结束时进行的。
(b)储存位置
全局区的位置通常在可执行程序的数据段或BSS段(Block Started by Symbol)中。BSS段用于存储未初始化的全局变量和静态变量,而数据段用于存储初始化的全局变量和静态变量。
示例代码:
#include <stdio.h> #include <sdtlib.c> int a; //a会初始化为0 int b = 10; static int 20; main(){ ...... }
(4)常量区(Constant)
(a)用途
常量区是计算机程序内存中的一个区域,用于存储常量数据,这些数据在程序执行期间不会被修改。
示例代码:
const *char = "hello world" const int = 9; static const double = 34.2;
注意:宏定义不占用任何的空间,只是使用宏时替换该值
(5)代码区(Code)
存储机器代码:
- 代码区存储了程序的机器代码,这是由编译器将源代码翻译成的二进制指令。这些指令是计算机的中央处理单元(CPU)直接执行的。
只读性:
- 通常情况下,代码区中的指令是只读的,不允许在运行时修改。这是为了确保程序的完整性和安全性,以防止未经授权的更改。
静态分配:
- 代码区的大小在程序编译时确定,并且在程序执行期间保持不变。这与栈区和堆区不同,后者用于动态分配内存。
可执行文件:
- 在可执行文件中,代码区通常存储在文件的一个段(section)中。操作系统在加载可执行文件时将代码区的内容加载到内存中,并开始执行程序的指令。
指令执行:
- CPU从代码区中读取指令,按照它们的顺序执行,从而实现程序的功能。这包括计算、分支、跳转和其他各种操作。
地址访问:
- 程序内部的跳转、分支和函数调用指令使用代码区中的地址来访问和执行不同的代码段。这些地址通常是相对于代码区的起始地址计算的。
不同的代码段:
- 代码区通常包含程序的各种函数、方法和控制结构的代码段。这些代码段由编译器生成,并根据需要在程序执行期间调用。
保护:
- 由于代码区是只读的,它通常受到操作系统的保护,以防止恶意软件或未经授权的访问和修改。这有助于确保程序的安全性。
(在c++中的虚函数表是放在代码区的)
总结:
栈区——局部变量、调用函数
堆区——动态分配(需要申请内存)
全局区——全局变量和静态变量(static)
常量区——只读不改(const)
代码区——只读、静态分配、受保护的、机器代码指令