在C语言中,对内存进行了划分,总共分为:栈区、堆区、代码区、常量区、全局数据区。其中,全局数据区又可细分为:初始化静态数据区和未初始化静态数据区。
一、栈区
- 存放函数执行时的局部变量、函数的参数和函数返回值。
- 栈区的大小由操作系统分配。
- 函数之间的调用就是通过栈实现的:调用函数就入栈,函数执行完就出栈。
- 栈区的数据由编译器自动分配和释放内存,不需要程序员手动干预。
- 栈区按内存地址由高到低方向生长。也就是说,新分配的栈内存地址会比之前的低。
- 栈区的操作非常高效、速度快,因为在栈空间的分配和释放只涉及移动栈指针。使得栈区存储临时变量和函数调用信息的理想选择。
二、堆区
- 堆区是动态内存分配的区域。可以在程序运行过程中申请(malloc)和释放(free)内存空间。与栈区不同,堆区上的内存需要程序员手动管理。在申请的内存区域使用完成后要及时释放,如果不能很好的控制内存使用,会造成内存泄露的风险。
- 与栈区另外的一个不同点,堆区申请的空间没有大小限制,大小由系统内存或虚拟内存上限决定。
- 堆区的内存分配和释放通常比栈区慢,这是因为在堆区维护了一个内存块记录堆区的使用情况,以便在分配和释放内存时能够高效地找到合适的内存块,同时修改堆区使用信息,由于堆区还会涉及到内存碎片的整理,这些都会影响到内存的分配和释放性能。
- 堆区的内存地址通常是从低地址向高地址方向增长的。也就是说,新分配的内存块会位于已分配内存块的上方。
三、代码区
- 存放要执行的编译后的二进制代码。这部分内存是只读的。
- 代码区是共享的,多个进程共享同一份代码。
四、常量区
- 存放字符串常量(由指针方式创建的字符串,使用数组方式初始化的字符串不放在这个区)和const修饰的全局变量。这部分数据存放在(.rodata段)。
- 常量区存储内容固定,一般在编译时就已经确定,并且在整个程序运行期间都不能改变,是只读的;由于不可以修改,所以在代码编译过程中会做一些优化,读取效率会非常高。
五、全局数据区
- 存放全局变量和静态变量(由static修饰的全局变量或局部变量)。这部分数据在整个程序生命周期都存在,只有程序运行完成后才会销毁。
- 初始化的全局变量和静态变量存放在(.data),没有初始化的全局变量和没有初始化的静态变量存放在(.bss)区。
- 全局变量可以在程序的任何地方访问,局部静态变量只能在声明它的函数内访问,但是函数调用后静态变量仍然存在,它的内存空间不会释放。
在C语言的内存划分中,最重要的两个区域就是堆区和栈区了,如果清楚了这两个区域的特点和使用场景,就掌握了C语言的内存精髓。
内存空间的生长方向:栈区由高地址向低地址方向生长,堆区由低地址向高地址方向生长。
下面通过代码观察c语言的各个区域信息:
c
#include <stdio.h>
#include <stdlib.h>
int gi = 10;
long gl = 20L;
static int s_gi = 10;
static long s_gl = 20L;
const int c_gi = 10;
const long c_gl = 20L;
int gi1;
long gl1;
static int s_gi1;
static long s_gl1;
const int c_gi1;
const long c_gl1;
void func1(int i) {
int ii;
printf("method parameter : %p\n", &i);
printf("method local variable : %p\n", &ii);
}
void func2(long l) {
long ll;
printf("method parameter : %p\n", &l);
printf("method local variable : %p\n", &ll);
}
int main() {
// 栈区
printf("-------- Stack Memory --------\n");
int i = 10;
int l = 20L;
printf("local variable : %p\n", &i);
printf("local variable : %p\n", &l);
const int c_i = 10;
const long c_l = 20L;
printf("const local variable : %p\n", &c_i);
printf("const local variable : %p\n", &c_l);
func1(i);
func2(l);
// 堆区
printf("\n-------- Heap Memory --------\n");
int* pi = malloc(sizeof(int));
long* pl = malloc(sizeof(long));
printf("%p\n", pi);
printf("%p\n", pl);
// 已初始化全局变量或静态变量
printf("\n-------- Data Segment --------\n");
static int s_i = 10;
static long s_l = 20;
printf("static local variable : %p\n", &s_i);
printf("static local variable : %p\n", &s_l);
printf("init global variable : %p\n", &gi);
printf("init global variable : %p\n", &gl);
printf("init static global variable : %p\n", &s_gi);
printf("init static global variable : %p\n", &s_gl);
// 未初始化全局变量或静态变量
printf("\n-------- Block Started by Symbol --------\n");
static int s_i1;
static long s_l1;
printf("uninit static local variable : %p\n", &s_i1);
printf("uninit static local variable : %p\n", &s_l1);
printf("uninit global variable : %p\n", &gi1);
printf("uninit global variable : %p\n", &gl1);
printf("uninit static global variable : %p\n", &s_gi1);
printf("uninit static global variable : %p\n", &s_gl1);
// 代码
printf("\n-------- Code Segment --------\n");
printf("%p\n", func1);
printf("%p\n", func2);
// 常量
printf("\n-------- Const Data --------\n");
char* pstr1 = "Hello,world!";
char* pstr2 = "Hello,world!";
char* pstr3 = "你好,世界!";
printf("const string variable : %p\n", pstr1);
printf("const string variable : %p\n", pstr2);
printf("const string variable : %p\n", pstr3);
printf("init const global variable : %p\n", &c_gi);
printf("init const global variable : %p\n", &c_gl);
printf("uninit const global variable : %p\n", &c_gi1);
printf("uninit const global variable : %p\n", &c_gl1);
free(pi);
free(pl);
return 0;
}
程序执行输出:
-------- Stack Memory --------
local variable : 00000008c31ff8a4
local variable : 00000008c31ff8a0
const local variable : 00000008c31ff89c
const local variable : 00000008c31ff898
method parameter : 00000008c31ff870
method local variable : 00000008c31ff85c
method parameter : 00000008c31ff870
method local variable : 00000008c31ff85c
-------- Heap Memory --------
000001c2b4a01c10
000001c2b4a01c30
-------- Data Segment --------
static local variable : 00007ff7b0efa010
static local variable : 00007ff7b0efa014
init global variable : 00007ff7b0efa000
init global variable : 00007ff7b0efa004
init static global variable : 00007ff7b0efa008
init static global variable : 00007ff7b0efa00c
-------- Block Started by Symbol --------
uninit static local variable : 00007ff7b0eff040
uninit static local variable : 00007ff7b0eff044
uninit global variable : 00007ff7b0eff030
uninit global variable : 00007ff7b0eff034
uninit static global variable : 00007ff7b0eff038
uninit static global variable : 00007ff7b0eff03c
-------- Code Segment --------
00007ff7b0ef1634
00007ff7b0ef166f
-------- Const Data --------
const string variable : 00007ff7b0efb257
const string variable : 00007ff7b0efb257
const string variable : 00007ff7b0efb264
init const global variable : 00007ff7b0efb000
init const global variable : 00007ff7b0efb004
uninit const global variable : 00007ff7b0efb008
uninit const global variable : 00007ff7b0efb00c