5.C语言内存分区-堆-栈
目录
内存分区
运行之前
代码区
全局初始化数据区 、静态数据区 (data)
未初始化数据区(bss(Block Started by Symbol)区)
总结
运行之后
代码区 (text segment)
未初始化数据区(bss)
全局初始化数据区,静态数据区(data segment)
栈区(stack)
堆区(heap)
内存分区
运行之前
如果要执行一个C程序,那么第一步需要对这个程序进行编译。
1 | 预处理 | 宏定义展开,头文件展开,条件编译,这里不会检查语法 |
2 | 编译 | 检查语法,将预处理后的文件编译成汇编文件 |
3 | 汇编 | 将汇编文件生成目标文件(二进制) .o文件已生成 |
4 | 链接 | 将目标文件链接为可执行程序 二进制文件转换可执行文件 类似.ext |
当编译完成生成可执行文件之后,我们可以通过linux下的size买了查看一个可执行二进制文件基本情况:
通过上图可以得知,在没有运行程序前,也就是说,程序没有加载到内存前,可执行程序内部已分好3段信息,分别是 代码区(text) , 数据区(data) 和未初始化数据区(bss) 3个部分(可以把data和bss合起来叫做静态区,或者全局区)
以下是细分:
bss区域放未初始化的数据如: static int a; //未初始化数据。
static int a = 10 ;//这个时候数据放在数据区 data区。
代码区
存放CPU执行的机器质量。通常代码是可以共享的(即另外的执行程序可以调用它),使其可共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。代码通常是只读的,使其只读的原因是防止程序意外的修改他的指令。另外,代码区还规划了局部变量的相关信息。
说白了,代码区,就是放代码的
以上重点
共享:比如我们创建了一个a.exe和a1.exe两个代码是一样然后,第一次点击a.exe ,第二次点击a1.ext其实运行的还是a.exe原因是代码一样,共享
只读:比如我们在开发一个游戏币,创建了游戏币和人名币两个变量,如果是可写的,那么吧游戏币写到人名币里面那这样就是大事故,所以设置成只读。
全局初始化数据区 、静态数据区 (data)
该区包含了在程序中明确被初始化的全局变量,已经初始化的静态变量(包括全局静态变量)和常量数据(字符串常量)
未初始化数据区(bss(Block Started by Symbol)区)
存入的是全局未初始化变量和未初始化静态变量。未初始化数据区的数据在程序开始执行之前被内核初始化未0后者空(NULL)
总结
程序源代码被编译之后主要分成两段: 程序指令(代码区) 和 程序数据(数据区) 。代指码段属于程序指令,而数据域段和bss段属于程序数据
为什么要分开?
程序被加载到内存中之后,可以将数据和代码分别映射到两个内存区域。由于数据区域对进程来说是可读可写的,而指令域对程序来说是只读的,所以区分之后,可以将程序指令区域和数据区域分别设置成可读可写或者只读。这样可以防止程序的有意或者无意被修改
当程序中运行着多个同样的程序的时候,这些程序执行的指令都是一样的,所以只需要内中保存一份程序的指令就可以了,只是每一个程序运行中数据不一样而已,这样可以节省大量的内存。
运行之后
程序在加载到内存前,代码区和全局区(data 和 bss)的大小就是固定的,程序运行期间不能改变。然后,运行可执行程序,操作系统吧物理硬盘程序,加载到内存,除了根据可执行程序的信息分出代码区(text) , 数据区(data) 和未初始化数据区(bss)之外,还额外增加了栈区,堆区
代码区 (text segment)
加载的是可执行文件代码段,所有的可执行代码都加载到代码区,这块内存是不可以在运行期间修改的
案例:
int main() {int a = 1; // 这一行对应的机器指令就存储在代码区return 0;
}
未初始化数据区(bss)
加载的是可执行文件bss段,位置可以分开亦可以紧靠数据段,存储于数据段的数据(全局未初始化,静态未初始化数据)的生命周期未整个程序运行过程。
案例
int a; // 存储在BSS段,默认值为0static int i; // 局部静态变量,默认值也为0,存储在BSS段
全局初始化数据区,静态数据区(data segment)
加载的是可执行文件数据段,存储于数据段(全局初始化,静态初始化数据,字符常量(只读))的数据的生存周期为整个程序运行过程。
案例
int a= 10; // 存储在数据段static int i= 20; // 局部静态变量,同样存储在数据段
栈区(stack)
栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值,返回值,局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。
栈的空间是有限的,尽量用完就释放掉
1是第一个进入。
如果1想出来,那要吧4先扔掉,3在扔掉,2在扔掉,才是1
可以认为吃米饭一样,先吃上面的,才能见碗底。
堆区(heap)
堆是一个大容器,它的容量要远远大于栈,但没有栈那样的先进后出的顺序。用于动态内存分配。堆在内存中位于bss区和栈区之间。一般由程序员分配和释放,若程序不释放,程序结束时由操作系统回收
大容量:大容量,到底有多大,要看机器有多好,看机器
分配:使用malloc函数分配
释放:使用free函数释放 ,如果不释放程序会在系统结束后回收 注意,一定要手动释放
生命周期
类型 | 作用域 | 生命周期 | 存储位置 |
auto变量 | 一对{}内 | 当前函数 | 栈区 |
static局部变量 | 一对{}内 | 整个程序运行期 | 初始化在data段,未初始化在BSS段 |
extern变量 | 整个程序 | 整个程序运行期 | 初始化在data段,未初始化在BSS段 |
static全局变量 | 当前文件 | 整个程序运行期 | 初始化在data段,未初始化在BSS段 |
extern函数 | 整个程序 | 整个程序运行期 | 代码区 |
static函数 | 当前文件 | 整个程序运行期 | 代码区 |
register变量 | 一对{}内 | 当前函数 | 运行时存储在CPU寄存器 |
字符串常量 | 当前文件 | 整个程序运行期 | data段 |
栈 注意事项
案例1
int* func() {int a = 10;return &a;
}void test01()
{int* p = func();printf("p = %d\n",p);
}
运行结果:
从上面结果来看,不是我们预期的结果,我们预期结果是 p = 10
为什么是这样?
首先我们来看func函数,函数定义的是int a = 10, 函数最终返回了a的地址,所以a在栈区的值已经释放了,我们没有去操作这一块内存。
案例2
char * getMyName()
{char myName[] = "达帮主";return &myName;
}void test02()
{char* p = getMyName();printf("my name p = %s\n",p);
}
运行结果:
问题与案例1一样,也是释放了,不要在意结果。
栈的释放过程
从上面图中可以看出,当getMyName方法运行完成之后,常量区的内容是会被释放的,放回p收到的只是地址。所以上面案例2是乱码,内容被释放,我们根本不知道是上面东西。
总结
不要返回局部变量地址,局部变量在函数执行之后就释放了,我们没有权限去操作释放后的内存。
堆 注意事项
案例1
int* getSpace() {//手动分配堆空间int *p = malloc(sizeof(int)*5);if (p == NULL) {return 0;}for (int i = 0; i < 5; i++) {p[i] = 1000 + i;}return p;
}void test01() {int* p = getSpace();for (int i = 0; i < 5; i++){printf("p:%d \n",p[i]);}//手动释放堆空间free(p);p = NULL; //防止野指针
}int main()
{test01();printf("\n\n");system("pause");return EXIT_SUCCESS;
}
运行结果:
从上面代码来看我们使用了malloc来分配空间,分配的内存是存在堆中,所以数据没释放是一直存在的。
案例2
void getMyName(char *pp)
{//分配内存char * temp = malloc(sizeof(char)*50);if (temp == NULL) {return;}memset(temp,0,50);//赋值strcpy_s(temp,50,"达帮主");pp = temp;
}void test02()
{char* p = NULL;getMyName(p);printf("%s\n",p);
}
运行结果
上面的原因是因为同级指针通过函数参数是无法修饰到p的,所以我们要在函数参数写二级指针。
如果主调函数中没有给指针分配内存,被调函数用同级指针是修饰不到主调函数中的指针的。
看下面案例
void getMyName(char **pp)
{//分配内存char * temp = malloc(sizeof(char)*50);if (temp == NULL) {return;}memset(temp,0,50);//赋值strcpy_s(temp,50,"达帮主");*pp = temp;
}void test02()
{char* p = NULL;getMyName(&p);printf("%s\n",p);
}
运行结果:
上下的区别是加入二级指针,以及传的是地址,最后吧分配的内存修饰给二级指针
流程图
总结
在理解C内存分区时,常会碰到术语:数据区,堆,栈,静态区,常量区,全局区,字符串常量区,文字常量区,代码区等等。在这里,尝试捋清楚以上分区的关系。
- 数据区包括:堆,栈,全局/静态存储区。
- 全局/静态存储区包括:常量区,全局区、静态区。
- 常量区包括:字符串常量区、常变量区。
- 代码区:存放程序编译后的二进制代码,不可寻址区。
可以说,C/C++内存分区其实只有两个,即代码区和数据区。
相关文章:

5.C语言内存分区-堆-栈
目录 内存分区 运行之前 代码区 全局初始化数据区 、静态数据区 (data) 未初始化数据区(bss(Block Started by Symbol)区) 总结 运行之后 代码区 (text segment) 未初始化数据区(bss) 全局初始化数据区,静态…...

传统CV算法——基于opencv的答题卡识别判卷系统
基于OpenCV的答题卡识别系统,其主要功能是自动读取并评分答题卡上的选择题答案。系统通过图像处理和计算机视觉技术,自动化地完成了从读取图像到输出成绩的整个流程。下面是该系统的主要步骤和实现细节的概述: 1. 导入必要的库 系统首先导入…...

国产 HighGo 数据库企业版安装与配置指南
国产 HighGo 数据库企业版安装与配置指南 1. 下载安装包 访问 HighGo 官方网站(https://www.highgo.com/),选择并下载企业版安装包。 2. 上传安装包到服务器 将下载的安装包上传至服务器,并执行以下命令: [rootmas…...

「Mac畅玩鸿蒙与硬件46」UI互动应用篇23 - 自定义天气预报组件
本篇将带你实现一个自定义天气预报组件。用户可以通过选择不同城市来获取相应的天气信息,页面会显示当前城市的天气图标、温度及天气描述。这一功能适合用于动态展示天气信息的小型应用。 关键词 UI互动应用天气预报数据绑定动态展示状态管理 一、功能说明 自定义…...

Springboot @Transactional使用时需注意的几个问题
一、事务的隔离级别 在Springboot应用中,如果我们想实现方法一旦执行有异常产生,就触发事务回滚,可以在方法上面添加Transactional注解。如果应用采用mysql数据库,虽然mysql本身也有事务隔离机制,但在Sping数据库的应…...

数字经济下的 AR 眼镜
目录 1. 📂 AR 眼镜发展历史 1.1 AR 眼镜相关概念 1.2 市面主流 XR 眼镜 1.3 AR 眼镜大事记 1.4 国内外 XR 眼镜 1.5 国内 AR 眼镜四小龙 2. 🔱 关键技术 2.1 AR 眼镜近眼显示原理 2.2 AR 眼镜关键技术 2.3 AR 眼镜技术难点 3. Ὂ…...

力扣150题
88. 合并两个有序数组 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。 **注意:**…...

剑指offer搜索二维矩阵
题目连接 https://leetcode.cn/problems/search-a-2d-matrix-ii/’ 代码 自己想出来的 解法一 初始化两个指针,i0,j列数-1 若此时matrix[i][j]target 则返回true 若此时matrix[i][j]>target,表明在第j列中不可能存在target,因为列是升序的 若此时ma…...

如何设置浏览器不缓存网页
设置浏览器不缓存网页可以通过多种方法实现,以下是一些常见的策略: HTTP响应头控制: Cache-Control:这是最常用的HTTP头之一,用于控制响应的缓存行为。例如: Cache-Control: no-cache, no-store, must-r…...

Iris简单实现Go web服务器
package mainimport ("github.com/kataras/iris" )func main() {app : iris.New() // 实例一个iris对象//配置路由app.Get("/", func(ctx iris.Context) {ctx.WriteString("Hello Iris")})app.Get("/aa", func(ctx iris.Context) {ct…...

后端项目java中字符串、集合、日期时间常用方法
我这里只介绍了项目中最常用的哈,比如像集合有很多,但我们最常用的就是ArrayList。 然后我这里会以javascript中的字符串、数组的方法为基准来实现,有些方法js和java会有些区别也会介绍 字符串 每次修改 String 对象都会创建一个新的对象,而 StringBuffer 可以在同一个对象…...

【Spring事务】深入浅出Spring事务从原理到源码
什么是事务 保证业务操作完整性的一种数据库机制 (driver 驱动)事务特定 ACID A 原子性 (多次操作 要不一起成功 要不一起失败 (部分失败 savepoint)) C 一致性 (事务开始时数据状态,…...

vue.js滑动到顶便锁定位置
<template><div><div class"nav"></div><div class"searchBar" id"searchBar"><ul :class"searchBarFixed true ? isFixed :"> <li>区域<i class"iconfont icon-jiantouxia"…...

EdgeX Core Service 核心服务之 Core Command 命令
EdgeX Core Service 核心服务之 Core Command 命令 一、概述 Core-command(通常称为命令和控制微服务)可以代表以下角色向设备和传感器发出命令或动作: EdgeX Foundry中的其他微服务(例如,本地边缘分析或规则引擎微服务)EdgeX Foundry与同一系统上可能存在的其他应用程序…...

掌握常用HTML标签:创建个人简介网页
任务目标 理解HTML文档的基本结构,掌握常见的HTML标签及其用途,创建一个简单的个人简介网页。 学习内容脑图 #mermaid-svg-5GTdqH41gawr4v0h {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

音视频学习(二十五):ts
TS(MPEG-TS,MPEG Transport Stream) 是一种广泛应用于流媒体传输和存储的容器格式。它最早由 MPEG(Moving Picture Experts Group)组织制定,用于视频和音频的压缩编码。在 HLS(HTTP Live Stream…...

10. 虚拟机VMware Workstation Pro下共享Ubuntu和Win11文件夹
本文记录当前最新版虚拟机VMware Workstation Pro(2024.12)如何在win11下共享文件,以实现Windows与Ubuntu互传文件的目的。 1. 创建共享文件夹 1.1 先关闭虚拟机的客户机,打开虚拟机设置 1.2 在虚拟机设置界面找到“选项”->“…...

单元测试mock框架Mockito
为了继续改进 Mockito 并进一步改善单元测试体验,我们希望您升级到 2.1.0!Mockito 遵循语义版本控制,仅在主要版本升级时包含重大更改。在库的生命周期中,重大更改是推出一组全新功能所必需的,这些功能会改变现有行为甚…...

Python从0到100(七十八):神经网络--从0开始搭建全连接网络和CNN网络
前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…...

2024多模态大模型综述最新总结
摘要 随着人工智能技术的快速发展,多模态大模型(MLLM)已成为研究的新热点。这些模型以强大的大型语言模型(LLM)为基础,能够处理和理解多种模态信息,如文本、图像、视频和音频。本文综述了MLLM的…...

Redis——缓存穿透
文章目录 1. 问题介绍1.1 定义1.2 举例 2. 解决方案2.1 方案一:空值缓存2.1.1 做法2.1.2 举例2.1.3 示例代码2.1.4 优点2.1.5 缺点 2.2 方案二:布隆过滤器2.2.1 思想2.2.2 做法2.2.3 示例代码2.2.4 优点2.2.5 缺点 2.3 方案三:限流3. 总结 1.…...

1.gitlab 服务器搭建流程
前提条件: 一、服务器硬件水平 搭建gitlab服务器最低配置要求2核4G,低于这个配置的服务器运行效果很差。 gitlab官网:https://about.gitlab.com/ 下载地址:gitlab/gitlab-ce - Packages packages.gitlab.com 本机ubuntu 二、安装依赖 su…...

McDonald‘s Event-Driven Architecture 麦当劳事件驱动架构
原文链接 1 mcdonalds-technical-blog/ 原文链接 2 mcdonalds-technical-blog/ 麦当劳在异步、事务性和分析性处理用例中使用跨技术栈的事件,包括移动订单进度跟踪和向客户发送营销通信(交易和促销)。 统一事件平台(unified eve…...

GTID详解
概念和组成 1,全局事务表示:global transaction identifiers 2, GTID和事务一一对应,并且全局唯一 3,一个GTID在一个服务器上只执行一次 4,mysql 5.6.5开始支持 组成 GTID server_uuid:transaction_id 如…...

图解HTTP-HTTP状态码
状态码 状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。 类别原因短语1XXInformational(信息状态码)接收的请求正在处理2XXSuccess(成功状态码)请求正常处理完毕4XXRedirection (重定向状态码)需要…...

sh cmake-linux.sh -- --skip-license --prefix = $MY_INSTALL_DIR
本文来自天工AI --------- 命令用于安装CMake的脚本,其中--skip-license参数表示跳过许可协议的显示,--prefix参数指定了CMake的安装目录。$MYINSTALLDIR是一个环境变量,应该在运行命令之前设置为您想要安装CMake的目录。 -------- sh xx…...

MySQL 在window免安装启动
复制my.ini文件 初始化命令:mysqld --initialize --console 执行启动bat:启动mysql.bat 主要命令是:mysqld --standalone 免密码启动mysql: mysqld --defaults-file"D:\xxx\soft\mysql-8.0.40-winx64\my.ini" …...

[JavaScript] 我该怎么去写一个canvas游戏
首先你得知道canvas的基础语法,此处不过多赘述. 一、如何更新视图 canvas里面有个clearRect方法,可以遮住画布中一个矩形部分. 但是你想这样做就难免会遮住一些本不该遮住的东西,因为它是一个矩形,并且你还要计算它的位置和尺寸…...

【潜意识Java】深度解析黑马项目《苍穹外卖》与蓝桥杯算法的结合问题
目录 为什么要结合项目与算法? 1. 蓝桥杯与《苍穹外卖》项目的结合 实例:基于蓝桥杯算法思想的订单配送路径规划 问题描述: 代码实现:使用动态规划解决旅行商问题 代码解析: 为什么这个题目与蓝桥杯相关&#x…...

python报错系列(16)--pyinstaller ????????
系列文章目录 文章目录 系列文章目录前言一、pyinstaller ????????1.报错如下2.安装pyinstaller3.报错如下:4.封装py文件为exe成功5.国内源 总结 前言 一、pyinstaller ??? 1.报错如下 PS D:\Users\gxcaoty\Desktop\性能覆盖率> pyinstaller37.exe…...