当编写c 程序时,合理地使用内存分配是非常重要的。以下是一份c 内存分布框架梳理,帮助你更好地理解和管理内存:
先给上一个内存分布图,从上到下,内存地址依次降低。
-------------------------------------------------------
| high address |
--------------------------------------------------------
| heap |
| (动态分配) |
|--------------------------------------------------- --|
| 堆栈 |
| (局部变量、函数参数等) |
|-----------------------------------------------------|
| 全局/静态存储区 |
| (全局变量、静态变量) |
|------------------------------------------------------|
| 常量存储区 |
| (常量值) |
|------------------------------------------------------|
| 代码区 |
| (执行代码) |
-------------------------------------------------------
| low address |
--------------------------------------------------------
栈(stack):
栈上的内存分配是自动的,由编译器负责管理。
用于存储函数的局部变量、函数参数、函数调用的返回地址等。
栈的大小通常较小,适合存储相对较小的数据。
避免在栈上分配过大的数据,以免造成栈溢出。
#include
// 函数声明
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
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
// 全局变量
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
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
// 函数声明
void sayhello();
int main() {
// 调用函数
sayhello();
return 0;
}
// 函数定义
void sayhello() {
std::cout << "hello, world!" << std::endl;
}
在这个示例中:
sayhello() 函数的定义包含了要执行的指令,这些指令将在程序执行时加载到内存的代码区中。
在 main() 函数中调用了 sayhello() 函数,这会导致程序跳转到代码区中 sayhello() 函数的地址,并执行其中的指令,最终输出 “hello, world!”。
在编译后的可执行文件中,函数的机器码指令将存储在代码区,等待被执行。
总的来说,希望大家看了后,能对内存分布有个简单的了解,合理地使用内存分配结构可以提高程序的性能和稳定性,同时避免常见的内存相关问题。
在编写c 程序时,应该深入理解每种内存分配结构的特点和使用方式,以及注意内存管理的细节,从而编写出高效、稳定的程序。