菜鸟笔记
提升您的技术认知

c 内存分布-ag真人游戏

当编写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 程序时,应该深入理解每种内存分配结构的特点和使用方式,以及注意内存管理的细节,从而编写出高效、稳定的程序。

网站地图