内存与变量存储
内存的分区
在C语言中,进程的内存空间可以被分为以下几个主要部分:
这些内存分区通常被称为虚拟内存分区,因为它们是在程序运行时由操作系统和编译器管理的逻辑上的分区,而不是物理上的。
了解内存分区对于理解程序和内存管理是非常重要的,各个分区的详情如下:
-
堆区(Heap):堆区是动态分配内存的区域,程序员可以通过
malloc()
、calloc()
等函数在堆上动态分配内存,并通过free()
函数释放堆上的内存。堆区的大小在程序运行时可以动态变化。 -
栈区(Stack):栈区用于存储函数的局部变量、函数参数以及函数调用的返回地址等信息。每次函数调用时,都会在栈上分配一定的空间。栈是一种后进先出(LIFO)的数据结构。栈上的内存分配和释放由编译器自动管理,不需要程序员显式地进行操作。
-
全局区(Global Area):全局区包括了全局变量和静态变量的存储空间。全局变量在程序启动时被初始化,一直存在于整个程序的运行过程中。静态变量也存储在全局区中,但其作用域仅限于定义它的源文件。全局区在程序运行期间一直存在,直到程序结束。
-
文字常量区(Text Constants Area):文字常量区存储不可修改的常量值,包括字符串常量、字符常量、整数常量、浮点数常量等。这些常量在程序编译时被确定,并分配到文字常量区,在程序执行期间一直存在。文字常量区通常是只读的,因此任何试图修改文字常量区中的值的操作都会导致错误。这种只读的特性保证了程序中的常量数据的安全性和一致性。
-
代码区(Code Segment):代码区存储了程序的机器代码,也称为指令区或者文本区。在程序执行时,CPU会从代码区读取指令并执行。代码区通常是只读的,因为程序的机器代码在运行时不应该被修改。
这些区域的具体实现和分配可能会因编译器、操作系统和具体的程序而有所不同,但通常遵循类似的规则和概念。
变量的存储类别
在C语言中,变量的存储类别决定了变量的存储位置、生命周期和作用域。我们就这几个方面对各个存储类型的变量做详细介绍。
普通局部变量
作用范围 | 当前代码块内部 |
---|---|
生命周期 | 从其定义的位置开始,到其所在的代码块执行完毕为止。 |
存储区域 | 栈区 |
注意:
- 代码块由一对花括号
{}
包围的一组语句构成。 - 如果全局变量和局部变量同名,遵循就近原则。
- 普通局部变量不初始化其内容不确定。
案例:
#include <stdio.h>
int data = 10; // 全局变量
void testo1()
{
int data = 20; // 局部变量
printf("%d\n", data); // 就近原则
return;
}
int main(int argc, char const *argv[])
{
testo1();
return 0;
}
运行结果:
20
普通全局变量
作用范围 | 当前源文件或其源文件 |
---|---|
生命周期 | 从程序启动时创建该变量开始,直到程序结束时销毁为止。 |
存储区域 | 全局区 |
注意:
- 普通全局变量不初始化,默认为0
- 如果需要使用其他源文件的全局变量需要加extern
案例:
int data = 10; // 在其他源文件中定义全局变量
//test_extern.c
#include <stdio.h>
extern int data; // 声明全局变量
void testo1()
{
printf("%d ", data);
return;
}
int main(int argc, char const *argv[])
{
testo1();
return 0;
}
//test.c
运行结果:
10
因为涉及到两个源文件,在编译的时候也需要编译两个源文件
静态局部变量
作用范围 | 当前代码块 |
---|---|
生命周期 | 从程序启动时创建该变量开始,直到程序结束时销毁为止。 |
存储区域 | 全局区 |
注意:
- 静态局部变量不初始化内容为0
- 静态局部变量只会定义一次
案例:
#include <stdio.h>
void testo1()
{
static int data = 10; // 定义静态局部变量
data++;
printf("data=%d\n", data);
return;
}
int main(int argc, char const *argv[])
{
// 多次调用test01()
testo1();
testo1();
testo1();
testo1();
return 0;
}
运行结果:
data=11
data=12
data=13
data=14
静态全局变量
作用范围 | 当前源文件 |
---|---|
生命周期 | 从程序启动时创建该变量开始,直到程序结束时销毁为止。 |
存储区域 | 全局区 |
注意:
- 静态局部变量不初始化内容为0
- 只能在当前源文件使用
案例:
static data = 10; // 在其他源文件中定义静态全局变量
//test_extern.c
#include <stdio.h>
extern extern int data; // 声明静态全局变量
void testo1()
{
printf("data=%d\n", data);
return;
}
int main(int argc, char const *argv[])
{
testo1();
return 0;
}
//test.c
编译错误:
test.c:2:1: error: duplicate ‘extern’
2 | extern extern int data; // 声明静态全局变量
| ^~~~~~
test_extern.c:1:8: warning: type defaults to ‘int’ in declaration of ‘data’ [-Wimplicit-int]
1 | static data = 10; // 在其他源文件中定义静态全局变量
寄存器变量
作用范围 | 当前代码块 |
---|---|
生命周期 | 从其定义的位置开始,到其所在的代码块执行完毕为止。 |
存储区域 | 寄存器 |
注意:
- 虽然寄存器变量的声明要求编译器将其存储在寄存器中,但是实际情况可能会因为寄存器数量的限制、编译器的实现以及编译器的优化策略等因素而不同。如果编译器无法将变量存储在寄存器中,它会像普通的自动变量一样存储在内存中。