文章目录
- 1. 栈区(Stack)
- 2. 堆区(Heap)
- 3. 静态区(Static)
- 4. 常量区(Read-Only or Constant Section)
- 总结:
- 栈(Stack)的生长:
- 堆(Heap)的生长:
- 测试
- 一答案
- 二答案
1. 栈区(Stack)
栈区用于存放局部变量和函数调用信息,它的内存由编译器自动分配和释放,具有自动管理的特点。栈区的内存分配遵循先进后出的原则,生命周期是非常短暂的。
存储内容:
局部变量(例如函数中的局部变量、参数)
函数的返回地址、调用信息
临时对象(比如函数返回值、临时创建的变量
特点:
自动管理:栈上的变量在函数结束时自动释放,不需要显式的内存管理。
高效:栈的分配和释放效率很高,通常通过直接的指针调整完成。
生命周期短:栈上变量的生命周期限于作用域,出了作用域即被释放。
2. 堆区(Heap)
堆区用于存放程序运行时动态分配的内存,程序员需要手动管理(分配和释放)。C++中可以通过new分配堆内存,通过delete释放。
存储内容:
动态分配的内存,如通过new分配的对象或数组。
特点:
手动管理:需要显式使用new分配内存,delete释放内存,防止内存泄漏。
灵活性:堆上的内存可以在程序运行时根据需求动态分配,存储的数据不受作用域限制,可以在多个函数之间共享。
3. 静态区(Static)
静态区用于存放全局变量、静态变量(static关键字修饰的变量)和静态成员变量。这些变量在程序开始时分配内存,并在程序结束时释放,具有全局的生命周期。
存储内容:
全局变量:即作用域为整个程序的变量。
静态变量:局部静态变量在第一次进入所在函数时初始化,并且在后续调用中保留其值。
静态成员变量:类的static成员,属于类而非实例对象。
特点:
全局生命周期:静态区中的变量在程序运行期间始终存在,直到程序结束才被释放。
一次性初始化:静态变量在程序运行时或第一次使用时初始化,且仅初始化一次。
4. 常量区(Read-Only or Constant Section)
常量区用于存放程序中不可修改的常量数据。这个区域通常是只读的,防止程序试图修改这些常量,任何修改尝试都会导致程序异常(如段错误)。
存储内容:
字符串常量:例如const char* str = “hello”;中的"hello"字符串存放在常量区。
const修饰的全局变量:如const int a = 10;(注意:const修饰的局部变量在栈区)。
编译器决定存储的常量数据:编译器可能将某些不变的常量数据(如浮点数、整型常量等)放在常量区。
特点:
只读:程序无法修改常量区的内容,通常对这些区域的写操作会导致运行时错误。
共享性:常量区的数据可以被多个函数或模块共享,不重复存储。
总结:
下面是自己画的简易图:
栈区以及堆区旁边蓝色的箭头表示两者的生长方向
栈:从内存的顶部(高地址)开始,逐渐向下(低地址)分配内存。
堆:从内存的底部(低地址)开始,逐渐向上(高地址)分配内存。
栈(Stack)的生长:
生长方向:栈一般是向下生长的,即从高地址向低地址增长。这种分配是由编译器自动管理的,栈主要用于存储局部变量、函数调用帧(包括函数参数、返回地址等)。
分配与释放:栈上的内存分配和释放是自动的,随着函数的调用与返回,内存会自动进行分配和回收。比如,当进入一个函数时,该函数的局部变量会被分配到栈上;当函数执行结束时,栈上的内存会自动释放,不需要手动管理。
特点:
内存分配效率高,因为是连续的。
容量有限,一般有固定的大小,如果超过栈的容量,就会导致栈溢出(Stack Overflow)。
堆(Heap)的生长:
生长方向:堆的生长方向通常是向上生长的,即从低地址向高地址增长。堆内存是通过动态内存分配函数(如 malloc、free,或者在C++中使用 new、delete)进行管理的。
分配与释放:堆上的内存需要手动管理,程序员必须显式地进行分配和释放。如果没有及时释放内存,可能会导致内存泄漏(memory leak)。另外,如果反复分配和释放内存,还可能产生内存碎片,降低内存使用效率。
特点:
堆的大小仅受系统可用内存的限制,理论上可以动态扩大。
动态分配的内存灵活性强,但内存分配速度比栈慢。
需要小心管理,否则容易出现内存管理错误,比如忘记释放内存导致内存泄漏。
测试
下面分享一道例题,方便读者自己进行测试
一答案
globalVar在哪里?
选项:C. 数据段(静态区)
全局变量(globalVar)是在数据段(静态区)分配内存的。
staticGlobalVar在哪里?
选项:C. 数据段(静态区)
静态全局变量(staticGlobalVar)也是在数据段(静态区)分配内存。
staticVar在哪里?
选项:C. 数据段(静态区)
静态局部变量(staticVar)在数据段(静态区)分配内存,并且生命周期贯穿整个程序执行。
localVar在哪里?
选项:A. 栈
局部变量(localVar)是在栈上分配的,它的生命周期仅限于函数调用期间。
num1在哪里?
选项:A. 栈
局部数组(num1)也是在栈上分配的。
char2在哪里?
选项:A. 栈
字符数组(char2)是在栈上分配的。
*char2在哪里?
选项:A. 栈
char2 是字符数组的名称,它在栈上分配,因此它的内容也在栈上。
pChar3在哪里?
选项:A. 栈
指针 pChar3 是局部变量,在栈上分配。
*pChar3在哪里?
选项:D. 代码段(常量区)
pChar3 指向的是一个字符串常量 “abcd”,而字符串常量存储在代码段(常量区)。
ptr1在哪里?
选项:A. 栈
指针 ptr1 是局部变量,存储在栈上。
*ptr1在哪里?
选项:B. 堆
ptr1 指向的是通过 malloc 动态分配的内存,存储在堆上。
二答案
sizeof(num1) = 40
num1 是一个包含 10 个 int 元素的数组。在大多数系统中,int 占用 4 字节,因此数组占用 40 字节(4 * 10 = 40)。
sizeof(char2) = 5
char2 是一个字符数组,包含 “abcd” 和终止字符 \0。因此,sizeof(char2) 返回 5。
strlen(char2) = 4
strlen(char2) 计算的是字符串 “abcd” 的长度,不包括终止字符 \0,所以返回 4。
sizeof(pChar3) = 8(在 64 位系统上)
pChar3 是一个指针,在 64 位系统上,指针的大小是 8 字节。如果是 32 位系统,则为 4 字节。
strlen(pChar3) = 4
pChar3 指向字符串常量 “abcd”,strlen(pChar3) 返回 4。
sizeof(ptr1) = 8(在 64 位系统上)
ptr1 是一个指针,大小为 8 字节(在 64 位系统上)。如果是 32 位系统,则为 4 字节。