Memory Segment

作者:郑沛坤

无论冯诺伊曼结构还是哈佛结构,都强调了现代计算机以存储器为核心。事实上无论代码和数据运行时都存放在计算机内存中,无论芯片内和芯片外的FLASH、RAM等都要进行统一编址,内存的地址是唯一确认的,理解C程序的内存布局图,有利于理解和调试C程序。

  1. 内存布局图;

    一个C程序编译时内存分为5大存储区:堆取、栈区、全局区、文字常量区和代码区。

    image-20201227213217752

    如图是stm32f4的存储器映射图,4G内存的区域被分为8个512M的存储块:代码、SRAM、外设等不同功能的区域。

    image-20201227212518120

  2. 栈区

    栈区分为主栈区和其他栈区,每个函数都有一个独立的栈区,每个不同的栈区就是一个相互独立的作用域,在不同的作用域,变量名可以重复。在执行函数时,函数中的局部变量就保存在栈区,函数调用结束后,CPU会自动释放栈区内存的使用权。

    如以下案例1所示,不同的函数区域内,可以定义相同的局部变量名,如func()和main()函数中都定义了相同的局部变量”n”。main()函数中打印值不一定是100的原因,即是CPU自动释放调用函数的栈区内存的使用权并不意味着栈区数据的销毁,栈区内存数据的销毁取决于此块内存是否被其他函数所使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int *func(){
    int n = 100;
    return &n;
    }
    int main(){
    int *pFunc = func();
    int n = *pFunc;
    printf("\n value = %d\n",n);
    return 0;
    }
  3. 全局区

    全局区保存的是全局变量(Global Variable)和静态变量(Static Variable)等,全局区的内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在,由CPU进行分配和善后。

    全局变量在声明时没有赋初值时,编译器会将他初始化为0。全局变量的作用域是整个工程,其他文件不能再定义一个与其相同名字的变量,可以使用”extern”外部声明后直接使用。

    静态变量由静态全局变量和静态局部变量两种。静态全局变量的作用域小于全局变量,static修饰的全局变量仅对此文件有效,其他文件不可访问,作用域仅在此文件中,解决了其他文件中使用同名变量的冲突,有效的降低了不同程序模块之间的耦合。静态局部变量不同于一般的局部变量,他不存在栈区内存中,保存在全局区内存中,即使函数返回,他的值也能继续保存,如果将案例1中的代码稍加修饰,main()函数中打印值可保证是100。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int *func(){
    static int n = 100;
    return &n;
    }
    int main(){
    int *pFunc = func();
    int n = *pFunc;
    printf("\n value = %d\n",n);
    return 0;
    }

    函数的使用方式和全局变量类似,如果用static修饰函数时,即是静态函数。和静态局部变量相似,静态函数只在声明他的文件中可见,其他文件不能引用此静态函数,而可以定义相同函数名的静态函数。

    函数名和数组名类似,表明了此函数的地址。

  4. 堆区

    和栈区内存不同,堆区内存由程序员自己管理,通常在栈区内存空间不足时使用。和栈相比,堆区的好处是内存空间足够大,但同时也有内存空间碎片化、分配效率较低的问题。