內存分區與生命週期
程序運行順序與變量生命週期
代碼運行思路
從main函數開始,代碼是一行一行運行的。(一個工程裏有且只有一個main函數)

生命週期
- 局部變量:
- 位置:在某個函數或塊的內部聲明的變量稱為局部變量。
- 作用域:它們只能被該函數或該代碼塊內部的語句使用。局部變量在函數外部是不可知的。
#include <stdio.h>
int add(int a,int b);
int main ()
{
/* 局部变量声明 */
int a, b;
int c;
/* 实际初始化 */
a = 10;
b = 20;
c = add(a,b);
printf ("value of a = %d, b = %d and c = %d\n", a, b, c);
return 0;
}
int add(int x,int y)
{
int z = x + y;
return z;
}
運行/觀察結果: 運行後會按輸出語句打印對應內容,變量值可結合初始化、賦值和函數調用順序推導。
- 全局變量
- 位置:全局變量是定義在函數外部,通常是在程序的頂部。
- 作用域:全局變量在整個程序生命週期內都是有效的,在任意的函數內部能訪問全局變量。
#include <stdio.h>
/* 全局变量声明 */
int g;
int main ()
{
/* 局部变量声明 */
int a;
/* 静态(全局)变量声明 */
static int b;
/* 实际初始化 */
a = 10;
b = 20;
g = a + b;
printf ("value of a = %d, b = %d and g = %d\n", a, b, g);
return 0;
}
運行/觀察結果: 運行後會按輸出語句打印對應內容,變量值可結合初始化、賦值和函數調用順序推導。
在程序中,局部變量和全局變量的名稱可以相同,但是在函數內,如果兩個名字相同,會使用局部變量值,全局變量不會被使用。
#include <stdio.h>
void tset();
/* 全局变量声明 */
int g = 20;
int main ()
{
/* 局部变量声明 */
int g = 10;
printf ("value of g = %d\n", g);
test();
return 0;
}
void test()
{
printf("value of g = %d\n",g);
}
運行/觀察結果: 運行後會按輸出語句打印對應內容,變量值可結合初始化、賦值和函數調用順序推導。
內存四區
暫時無法在飛書文檔外展示此內容
- 靜態存儲區(全局區) :可以分為rodata區data區和bss區,已經初始化的只讀常量被放在rodata,已經被初始化的非零變量被放在data區;沒有被初始化的或者值為零的變量被放在bss區,通常默認初始化為 0(表示數字的數據類型),'\0'(char類型) 和 NULL(指針類型)。
- 棧區(stack) :局部變量被存放在該區,如果不初始化局部變量,那麼局部變量是隨機值。容量很小。
- 堆區(heap) :由程序員開闢內存空間給變量,由程序員分配和釋放,如果程序員不進行釋放內存,則會內存泄漏,當程序結束後,系統會幫忙釋放沒有被釋放的內存。
- 代碼區 :存放 CPU 執行的機器指令,通常是隻讀的。
內存分區模型
C++程序在執行時,將內存大方向劃分為4個區域
- 代碼區:存放函數體的二進制代碼,由操作系統進行管理的
- 全局區:存放全局變量和靜態變量以及常量
- 棧區:由編譯器自動分配釋放, 存放函數的參數值,局部變量等
- 堆區:由程序員分配和釋放,若程序員不釋放,程序結束時由操作系統回收
內存四區意義:
不同區域存放的數據,賦予不同的生命週期, 給我們更大的靈活編程
程序運行前
在程序編譯後,生成了exe可執行程序,未執行該程序前分為兩個區域
代碼區:
存放 CPU 執行的機器指令
代碼區是共享的,共享的目的是對於頻繁被執行的程序,只需要在內存中有一份代碼即可
代碼區是只讀的,使其只讀的原因是防止程序意外地修改了它的指令
全局區:
全局變量和靜態變量存放在此.
全局區還包含了常量區, 字符串常量和其他常量也存放在此.
==該區域的數據在程序結束後由操作系統釋放==.
示例:
//全局变量
int g_a = 10;
int g_b = 10;
//全局常量
const int c_g_a = 10;
const int c_g_b = 10;
int main() {
//局部变量
int a = 10;
int b = 10;
//打印地址
cout << "局部变量a地址为: " << &a << endl;
cout << "局部变量b地址为: " << &b << endl;
cout << "全局变量g_a地址为: " << &g_a << endl;
cout << "全局变量g_b地址为: " << &g_b << endl;
//静态变量
static int s_a = 10;
static int s_b = 10;
cout << "静态变量s_a地址为: " << &s_a << endl;
cout << "静态变量s_b地址为: " << &s_b << endl;
cout << "字符串常量地址为: " << static_cast<const void*>("hello world") << endl;
cout << "字符串常量地址为: " << static_cast<const void*>("hello world1") << endl;
cout << "全局常量c_g_a地址为: " << &c_g_a << endl;
cout << "全局常量c_g_b地址为: " << &c_g_b << endl;
const int c_l_a = 10;
const int c_l_b = 10;
cout << "局部常量c_l_a地址为: " << &c_l_a << endl;
cout << "局部常量c_l_b地址为: " << &c_l_b << endl;
return 0;
}
運行結果:見下方打印結果圖;地址值每次運行可能不同,重點觀察局部變量、全局變量、靜態變量和常量所在區域的相對差異。
打印結果:

總結:
- C++中在程序運行前分為全局區和代碼區
- 代碼區特點是共享和只讀
- 全局區中存放全局變量、靜態變量、常量
- 常量區中存放 const修飾的全局常量 和 字符串常量
程序運行後
棧區:
由編譯器自動分配釋放, 存放函數的參數值,局部變量等
注意事項:不要返回局部變量的地址,棧區開闢的數據由編譯器自動釋放
示例:
int * func()
{
int a = 10;
return &a;
}
int main() {
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
int *p = func();
cout << *p << endl;
cout << *p << endl;
// 返回 0 表示程序正常结束。
return 0;
}
運行/觀察結果: 運行後會打印示例中的變量值或地址;地址值與運行環境有關,以同類對象的相對位置和指針變化為觀察重點。
堆區:
由程序員分配釋放,若程序員不釋放,程序結束時由操作系統回收
在C++中主要利用new在堆區開闢內存
示例:
int* func()
{
int* a = new int(10);
return a;
}
int main() {
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
int *p = func();
cout << *p << endl;
cout << *p << endl;
// 返回 0 表示程序正常结束。
return 0;
}
運行/觀察結果: 運行後會按輸出語句打印對應內容,變量值可結合初始化、賦值和函數調用順序推導。
總結:
堆區數據由程序員管理開闢和釋放
堆區數據利用new關鍵字進行開闢內存
new操作符
C++中利用==new==操作符在堆區開闢數據
堆區開闢的數據,由程序員手動開闢,手動釋放,釋放利用操作符 ==delete==
語法: new 数据类型
利用new創建的數據,會返回該數據對應的類型的指針
示例1: 基本語法
int* func()
{
int* a = new int(10);
return a;
}
int main() {
int *p = func();
cout << *p << endl;
cout << *p << endl;
//利用delete释放堆区数据
delete p;
//cout << *p << endl; //报错,释放的空间不可访问
return 0;
}
運行/觀察結果: 運行後會按輸出語句打印對應內容,變量值可結合初始化、賦值和函數調用順序推導。
示例2:開闢數組
//堆区开辟数组
int main() {
int* arr = new int[10];
for (int i = 0; i < 10; i++)
{
arr[i] = i + 100;
}
for (int i = 0; i < 10; i++)
{
cout << arr[i] << endl;
}
//释放数组 delete 后加 []
delete[] arr;
return 0;
}
運行/觀察結果: 運行後會按輸出語句打印對應內容,變量值可結合初始化、賦值和函數調用順序推導。