Bootstrap

C语言内存结构

在C语言中,对内存进行了划分,总共分为:栈区、堆区、代码区、常量区、全局数据区。其中,全局数据区又可细分为:初始化静态数据区和未初始化静态数据区。
c-memory

一、栈区

  1. 存放函数执行时的局部变量、函数的参数和函数返回值。
  2. 栈区的大小由操作系统分配。
  3. 函数之间的调用就是通过栈实现的:调用函数就入栈,函数执行完就出栈。
  4. 栈区的数据由编译器自动分配和释放内存,不需要程序员手动干预。
  5. 栈区按内存地址由高到低方向生长。也就是说,新分配的栈内存地址会比之前的低。
  6. 栈区的操作非常高效、速度快,因为在栈空间的分配和释放只涉及移动栈指针。使得栈区存储临时变量和函数调用信息的理想选择。

二、堆区

  1. 堆区是动态内存分配的区域。可以在程序运行过程中申请(malloc)和释放(free)内存空间。与栈区不同,堆区上的内存需要程序员手动管理。在申请的内存区域使用完成后要及时释放,如果不能很好的控制内存使用,会造成内存泄露的风险。
  2. 与栈区另外的一个不同点,堆区申请的空间没有大小限制,大小由系统内存或虚拟内存上限决定。
  3. 堆区的内存分配和释放通常比栈区慢,这是因为在堆区维护了一个内存块记录堆区的使用情况,以便在分配和释放内存时能够高效地找到合适的内存块,同时修改堆区使用信息,由于堆区还会涉及到内存碎片的整理,这些都会影响到内存的分配和释放性能。
  4. 堆区的内存地址通常是从低地址向高地址方向增长的。也就是说,新分配的内存块会位于已分配内存块的上方。

三、代码区

  1. 存放要执行的编译后的二进制代码。这部分内存是只读的。
  2. 代码区是共享的,多个进程共享同一份代码。

四、常量区

  1. 存放字符串常量(由指针方式创建的字符串,使用数组方式初始化的字符串不放在这个区)和const修饰的全局变量。这部分数据存放在(.rodata段)。
  2. 常量区存储内容固定,一般在编译时就已经确定,并且在整个程序运行期间都不能改变,是只读的;由于不可以修改,所以在代码编译过程中会做一些优化,读取效率会非常高。

五、全局数据区

  1. 存放全局变量和静态变量(由static修饰的全局变量或局部变量)。这部分数据在整个程序生命周期都存在,只有程序运行完成后才会销毁。
  2. 初始化的全局变量和静态变量存放在(.data),没有初始化的全局变量和没有初始化的静态变量存放在(.bss)区。
  3. 全局变量可以在程序的任何地方访问,局部静态变量只能在声明它的函数内访问,但是函数调用后静态变量仍然存在,它的内存空间不会释放。

在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
;