打印
[应用相关]

C 语言内存分区详解并在 Ubuntu 和树莓派下验证

[复制链接]
1749|53
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
慢醇|  楼主 | 2021-8-3 23:01 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本文内容:重温 C 语言程序里全局变量、局部变量、堆、栈等概念,并在ubuntu和树莓派系统中分别编程,输出信息进行验证。

使用特权

评论回复
沙发
慢醇|  楼主 | 2021-8-3 23:03 | 只看该作者
编译环境:Ubuntu 、树莓派
编写语言:C 语言
编译工具:gcc

使用特权

评论回复
板凳
慢醇|  楼主 | 2021-8-3 23:07 | 只看该作者
一、内存分区概念
1)C语言在内存中的分区

使用特权

评论回复
地板
慢醇|  楼主 | 2021-8-3 23:12 | 只看该作者

使用特权

评论回复
5
慢醇|  楼主 | 2021-8-3 23:22 | 只看该作者
注 1:C/C++ 不提供垃圾回收机制,因此需要对堆中的数据进行及时销毁,防止内存泄漏,使用 free 和 delete 销毁 new 和 malloc 申请的堆内存,而栈内存是动态释放。

使用特权

评论回复
6
慢醇|  楼主 | 2021-8-3 23:23 | 只看该作者
注 2:静态局部变量 和 静态全局变量 属于静态存储方式的量不一定就是静态变量。 例如:全局变量虽属于静态存储方式,但不一定是静态变量,必须由 static 加以定义后才能成为静态外部变量,或称静态全局变量。

把局部变量改变为静态变量后是改变了它的存储方式,即改变了它的生存期。
把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。

使用特权

评论回复
7
慢醇|  楼主 | 2021-8-3 23:27 | 只看该作者
//main.cpp
#include <iostream>
using namespace std;
#include <string.h>

int a = 0;  //全局初始化区
char *p1;  //全局未初始化区

int main(void)
{
    int b;  //栈
        char s[] = "abc";  //"abc"在常量区,s在栈上。
        char *p2;  //栈
        char *p3 = (char*)"123456";  //123456\0";在常量区,p3在栈上。
        static int c = 0;  //全局(静态)初始化区

        p1 = (char *)malloc(10);
        p2 = (char *)malloc(20); //分配得来的 10 和 20 字节的区域就在堆区。

        strcpy(p1, "123456");  //123456\0放在常量区,编译器可能会将它与 p3 所指向的"123456"优化成一个地方。

        delete p1, p2;
        return 0;
}
为了更加直观的了解各个区域的内容,看以下的代码:

使用特权

评论回复
8
慢醇|  楼主 | 2021-8-3 23:28 | 只看该作者
2)动 / 静态分配内存
一个程序被加载到内存中,这块内存首先就存在两种属性:静态分配内存和动态分配内存。

使用特权

评论回复
9
慢醇|  楼主 | 2021-8-3 23:29 | 只看该作者

使用特权

评论回复
10
慢醇|  楼主 | 2021-8-3 23:31 | 只看该作者
3)内存分区详解

使用特权

评论回复
11
慢醇|  楼主 | 2021-8-3 23:33 | 只看该作者
注:text 和 data 段都在可执行文件中,由系统从可执行文件中加载;而 bss 段不在可执行文件中,由系统初始化。
这三段内存就组成了我们编写的程序的本体,但是一个程序运行起来,还需要更多的数据和数据间的交互,否则这个程序就是死的,无用的。所以我们还需要为更多的数据和数据交互提供一块内存——堆栈。

使用特权

评论回复
12
慢醇|  楼主 | 2021-8-3 23:35 | 只看该作者
堆和栈都是动态分配内存,两者空间大小都是可变的。

使用特权

评论回复
13
慢醇|  楼主 | 2021-8-3 23:36 | 只看该作者

使用特权

评论回复
14
慢醇|  楼主 | 2021-8-3 23:39 | 只看该作者
说明:每个线程都会有自己的栈,但是堆空间是共用的。

使用特权

评论回复
15
慢醇|  楼主 | 2021-8-3 23:40 | 只看该作者
举例:``char* p = new char[20];
这行代码在 Heap 中开辟了 20 个 char 长度的空间,同时在 Stack 上压入了 p,指针变量 p 存在于栈上,其值为刚刚在堆上开辟的空间的首地址。

使用特权

评论回复
16
慢醇|  楼主 | 2021-8-3 23:42 | 只看该作者
这张图中所示内存空间,地址由下往上增长,分别标示了 .text、.data(gvar)、.bss、stack和heap的内存分部情况。

text、data(gvar)、bss 在内存中地址较低低的位置(low level address),而堆栈则在相对较高的位置。
堆(Heap)往高地址方向生长,栈(Stack)往低地址方向生长。

使用特权

评论回复
17
慢醇|  楼主 | 2021-8-3 23:45 | 只看该作者

使用特权

评论回复
18
慢醇|  楼主 | 2021-8-3 23:47 | 只看该作者
在 C\C++ 中,通常可以把内存理解为 4 个分区:栈、堆、全局/静态存储区和常量存储区。

下面我们分别简单地介绍一下各自的特点。

使用特权

评论回复
19
慢醇|  楼主 | 2021-8-3 23:50 | 只看该作者

通常是用于那些在编译期间就能确定存储大小的变量的存储区,用于在函数作用域内创建,在离开作用域后自动销毁的变量的存储区。通常是局部变量,函数参数等的存储区。他的存储空间是连续的,两个紧密挨着定义的局部变量,他们的存储空间也是紧挨着的。栈的大小是有限的,通常 Visual C++ 编译器的默认栈的大小为 1MB ,所以不要定义 int a[1000000] 这样的超大数组。

使用特权

评论回复
20
慢醇|  楼主 | 2021-8-3 23:51 | 只看该作者

通常是用于那些在编译期间不能确定存储大小的变量的存储区,它的存储空间是不连续的,一般由 malloc(或 new)函数来分配内存块,并且需要用 free(delete)函数释放内存。如果程序员没有释放掉,那么就会出现常说的内存泄漏问题。需要注意的是,两个紧挨着定义的指针变量,所指向的 malloc 出来的两块内存并不一定的是紧挨着的,所以会产生内存碎片。另外需要注意的一点是,堆的大小几乎不受限制,理论上每个程序最大可达 4GB 。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

117

主题

1187

帖子

5

粉丝