前言
程序代码中各个变量都存储在内存的哪里呢?这是一个非常有趣的问题
结论
- 堆:堆允许程序在运行时动态地申请某个大小的内存。一般由程序员分配释放;(使用
malloc/new
) - 栈:由编译器自动分配释放,存放函数的参数值,局部变量等值;
- 静态数据区(data bss) [不全面,全面的看下文]
- 静态存储区:一定会存在且不会消失,这样的数据包括常量、常变量(const 变量)、静态变量、全局变量等;
- 常量存储区:常量占用内存,只读状态,决不可修改,常量字符串就是放在这里的。
一 基本概念
1.1 变量的生命周期:
变量有效的时间段,只有在变量的生命周期内,才能访问该变量。有的变量的生命周期是函数运行期间,函数结束,变量就消失了。有的变量的生命周期是程序 运行期间,只要程序还未结束,变量就不会消失或销毁。
1.2 变量分类:
全局变量,局部变量,静态变量(静态全局变量和静态局部变量),寄存器 变量,外部变量。
// main.c
#include <stdio.h>
int a0 = 1; // 全局变量
static int a1; // 全局静态未初始化变量
static int a2 = 0; // 全局静态初始化变量
extern int a3; //全局初始化变量;定义在其他文件中,这里是file.c中
void fun(void);
int main()
{
fun();
return 0;
}
void fun(void)
{
int a4; //局部变量
volatile int a5; //局部易变变量
static int a6 = 1; // 局部静态变量
}
// file.c
#include <stdio.h>
int a3 = 1; //这里是已初始化全局变量
全局变量:全局变量的说明的位置在所有函数之外 ,整个程序可见,生命周期为整个程序运行期间,存储位置为静态存储区。
局部变量:在函数内部说明的变量为局部变量,只有在函数执行时,局部变量才存在, 当函数执行完退出后,局部变量随之消失。作用域为函数内部,存储空间为栈。
全局静态变量:与全局变量唯一不同的地方是别的文件不能访问该变量
局部静态变量:与局部变量的区别是,存储在静态区,整个程序运行期间有效,具有记忆功能,只初始化一次,默认初始化为零
寄存器变量:不像其他变量那样在内存中存放数据,而是在 CPU 的寄存器中暂存数据, 使用寄存器变量比使用内存变量的操作速度快得多。只有整型和字符型变量可定义为寄存器 变量。由于 CPU 中寄存器有限,尽量减少使用数量和和占用时间,用完马上释放;不能定义为全局变量,也不能定义在结构或者类中。
extern 关键字:为了解决全局变量和函数的共用问题,就引入了 extern 关键字。这样 只需在一个文件中定义全局变量和函数,在另一个文件中要用到这些变量和函数时,只需将那个文件中的变量和函数说明表复制过来,在前面加上 extern,告诉编译器,这些变量和函 数已经在别的文件中定义说明。
二 程序内存
一个程序本质上都是由BSS段、data段、text段(代码区)
三个组成的(部分人直接把data和bss合起来叫做静态区或全局区)。可以看到一个可执行程序在存储(没有调入内存)时分为代码段、数据区和未初始化数据区三部分。
1)代码区:
存放CPU执行的机器指令。存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域属于只读。在代码段中,也有可能包含一些只读的常数变量
2)data段(全局初始化数据区/静态数据区):
- 在程序中明确被初始化的全局变量
- 已经初始化的静态变量(包括全局静态变量和局部静态变量)
- 常量数据(如字符串常量)
3)bss段(未初始化数据区)
- 未初始化全局变量
- 未初始化静态变量
- 未初始化数据区的数据在程序开始执行之前被内核初始化为0或者空(NULL)。(这就是未初始化的全局和静态变量默认值为 0 的根源)
4)可执行程序在运行时又多出两个区域:栈区和堆区。
- 栈区:由编译器自动释放,存放函数的参数值、局部变量等。每当一个函数被调用时,该函数的返回类型和一些调用的信息被存放到栈中。然后这个被调用的函数再为他的自动变量和临时变量在栈上分配空间。每调用一个函数一个新的栈就会被使用。栈区是从高地址位向低地址位增长的,是一块连续的内存区域,最大容量是由系统预先定义好的,申请的栈空间超过这个界限时会提示溢出,用户能从栈中获取的空间较小。
- 堆区:用于动态分配内存,位于BSS和栈中间的地址区域。由程序员申请分配和释放。堆是从低地址位向高地址位增长,采用链式存储结构。频繁的malloc/free造成内存空间的不连续,产生碎片。当申请堆空间时库函数是按照一定的算法搜索可用的足够大的空间。因此堆的效率比栈要低的多。
三 例题
例题1)
int a0=1;
static int a1;
const static int a2=0;
extern int a3;
void fun(void)
{
int a4;
volatile int a5;
static int a6 = 1;
return;
}
- a0 :全局初始化变量;生命周期为整个程序运行期间;作用域为所有文件;存储位置为data段。
- a1 :全局静态未初始化变量;生命周期为整个程序运行期间;作用域为当前文件;储存位置为BSS段。
- a2 :全局静态初始化变量;生命周期为整个程序运行期间;作用域为当前文件;存储位置为data段。
- a3 :全局初始化变量;其他同a1。
- (定义的地方通常在不带extern的地方,如果有初始值就存在数据段,未初始化存bss段)
- a4 :局部变量;生命周期为fun函数运行期间;作用域为fun函数内部;储存位置为栈。
a5 :局部易变变量;生命周期为fun函数运行期间;作用域为fun函数内部;储存位置为栈。- a6:局部静态变量;生命周期为整个程序运行期间;作用域为fun函数内部;存储位置为data
例题2)
int a = 0; // 全局变量;生命周期为整个程序运行期间;作用域为整个工程;存储位置为静态区(data)
char *p1; // 全局变量;整个程序运行期间;整个工程;存储位置为静态区(bss)
static int x=10; // 全局静态变量;整个程序运行期间;当前文件;静态区(data)
void func(void)
{
int b; // 局部变量;func函数运行期间;func函数内部;栈
char s[] = "123"; // 局部变量;func函数运行期间;func函数内部;栈
char *p2; // 局部变量;func函数运行期间;func函数内部;栈
char *p3 = "hello world"; // 局部变量;func函数运行期间;func函数内部;栈
static int c = 0; // 局部静态变量;整个程序运行期间;作用域为func函数内部;静态区(data)
register int i = 0; // 寄存器变量;func函数运行期间;func函数内部;寄存器或栈
p1 = (char *)malloc(128); //p1指向堆上的内存,直到free
p2 = (char *)malloc(256); //p2指向堆上的内存,直到free
free(p1);
free(p2);
}
int main(void)
{
func();
printf("hello world\n");
return 0;
}
勘误:下面的main函数均为func函数
参考文献
[1] -天凉好秋-. C语言 内存管理. CSDN博客. 2017.
https://blog.csdn.net/u011616739/article/details/61621815
[2] 洛铭. 变量的类型、作用域、存储空间、生命周期. CSDN博客. 2019.
https://blog.csdn.net/weixin_42528287/article/details/85783662
[3] 苏xiao孬. static extern 局部变量 全局变量 生命周期 作用域. 简书. 2016.
https://www.jianshu.com/p/ce19ec49511c
[4] 锋邢天下. 变量类型,变量作用域,变量存储空间,变量生命周期. 博客园. 2019.
https://www.cnblogs.com/fengxing999/p/10956274.html