当编写C++程序时,合理地使用内存分配是非常重要的。以下是一份C++内存分布框架梳理,帮助你更好地理解和管理内存:
先给上一个内存分布图,从上到下,内存地址依次降低。
-------------------------------------------------------
| High Address |
--------------------------------------------------------
| Heap |
| (动态分配) |
|--------------------------------------------------- --|
| 堆栈 |
| (局部变量、函数参数等) |
|-----------------------------------------------------|
| 全局/静态存储区 |
| (全局变量、静态变量) |
|------------------------------------------------------|
| 常量存储区 |
| (常量值) |
|------------------------------------------------------|
| 代码区 |
| (执行代码) |
-------------------------------------------------------
| Low Address |
--------------------------------------------------------
栈(Stack):
栈上的内存分配是自动的,由编译器负责管理。
用于存储函数的局部变量、函数参数、函数调用的返回地址等。
栈的大小通常较小,适合存储相对较小的数据。
避免在栈上分配过大的数据,以免造成栈溢出。
#include <iostream>
// 函数声明
void display(int num);
int main() {
int x = 5; // 在栈上声明一个整型变量x,并初始化为5
int y = 10; // 在栈上声明一个整型变量y,并初始化为10
display(x); // 调用函数display,将x作为参数传递
return 0;
}
// 函数定义
void display(int num) {
// 在函数内部声明一个局部变量num,并将传入的参数值复制给它
std::cout << "The number is: " << num << std::endl;
}
当程序执行到一个函数时,函数的局部变量和参数被分配在栈上;当函数执行完毕时,栈上的这些变量被销毁。
堆(Heap):
堆上的内存分配需要手动管理,使用new关键字进行分配,使用delete关键字进行释放。
用于存储动态分配的数据,如动态数组、对象等。
避免内存泄漏,确保每次分配的内存都能被正确释放。
#include <iostream>
int main() {
int* ptr = nullptr; // 声明一个指向整型的指针,并初始化为nullptr
// 在堆上动态分配一个整型数组,包含5个元素
ptr = new int[5];
// 使用循环为数组赋值
for (int i = 0; i < 5; ++i) {
ptr[i] = i * 2;
}
// 打印数组的值
std::cout << "Array elements: ";
for (int i = 0; i < 5; ++i) {
std::cout << ptr[i] << " ";
}
std::cout << std::endl;
// 释放动态分配的内存
delete[] ptr;
ptr = nullptr;
return 0;
}
全局/静态存储区(Global/Static Storage Area):
存储全局变量和静态变量。
全局变量在程序启动时被初始化,并在程序的整个生命周期中都存在。
静态变量也类似,但只在声明它的文件内可见。
避免滥用全局变量,尽量减少全局变量的数量,以免影响程序的可维护性和扩展性。
#include <iostream>
// 全局变量
int globalVar = 10;
// 函数声明
void func();
int main() {
// 输出全局变量的值
std::cout << "Global variable: " << globalVar << std::endl;
// 调用函数
func();
// 再次输出全局变量的值
std::cout << "Global variable after function call: " << globalVar << std::endl;
return 0;
}
// 函数定义
void func() {
// 修改全局变量的值
globalVar = 20;
std::cout << "Global variable inside function: " << globalVar << std::endl;
// 静态变量
static int staticVar = 5;
staticVar++;
std::cout << "Static variable inside function: " << staticVar << std::endl;
}
代码运行结果:
Global variable: 10
Global variable inside function: 20
Static variable inside function: 6
Global variable after function call: 20
globalVar 是一个全局变量,它在程序的整个生命周期内存在,并且可以在程序的任何地方访问。
func() 函数中修改了全局变量 globalVar 的值,并且声明了一个静态局部变量 staticVar,它的生命周期与程序的生命周期相同,但作用域仅限于 func() 函数内部。
在 main() 函数中调用了 func() 函数,并输出了修改后的全局变量的值。
常量存储区(Constant Storage Area):
存储程序中的常量值,如字符串常量、全局常量等。
这部分内存在程序启动时就已经分配,并在整个程序的生命周期内都不会被修改。
避免在常量存储区中修改常量值,以免引发未定义的行为。
#include <iostream>
int main() {
// 字符串常量存储在常量存储区
const char* str = "Hello, world!";
// 输出字符串常量
std::cout << "String constant: " << str << std::endl;
return 0;
}
这块我经常搞混乱,有时候会把 str 变量的内存分配搞错,主要是带有*, 老是会理解成堆上的内存了,老想着去这个内存需不需要去释放。这个是栈内存,自己释放了哈。大家应该不会搞错,其实堆上内存都是动态的,一般都是new和malloc申请出来的。
在这个示例中:
“Hello, world!” 是一个字符串常量,它存储在常量存储区。
str 是一个指向字符常量的指针,它指向字符串常量 “Hello, world!” 的首地址。
当程序运行时,字符串常量 “Hello, world!” 被存储在常量存储区,而指针 str存储在栈上。输出指针 str 的值将显示字符串常量的内容。
代码区(Code Area):
存储程序的执行代码,包括函数体、指令等。
这部分内存在程序加载时分配,并在程序执行期间不会被修改。
避免在代码区中进行写操作,以免导致程序崩溃或其他严重问题。
#include <iostream>
// 函数声明
void sayHello();
int main() {
// 调用函数
sayHello();
return 0;
}
// 函数定义
void sayHello() {
std::cout << "Hello, world!" << std::endl;
}
在这个示例中:
sayHello() 函数的定义包含了要执行的指令,这些指令将在程序执行时加载到内存的代码区中。
在 main() 函数中调用了 sayHello() 函数,这会导致程序跳转到代码区中 sayHello() 函数的地址,并执行其中的指令,最终输出 “Hello, world!”。
在编译后的可执行文件中,函数的机器码指令将存储在代码区,等待被执行。
总的来说,希望大家看了后,能对内存分布有个简单的了解,合理地使用内存分配结构可以提高程序的性能和稳定性,同时避免常见的内存相关问题。
在编写C++程序时,应该深入理解每种内存分配结构的特点和使用方式,以及注意内存管理的细节,从而编写出高效、稳定的程序。