c语言内存块讲解
文章目录
- 前言
- 一、栈区
- 1、栈区的特点:
- 1.1 自动管理
- 1.2 后进先出
- 1.3 有限大小
- 1.4 高速访问
- 1.5 栈区存储方向
- 2、栈区使用注意事项
- 二、堆区
- 1、堆区的定义
- 2、堆区的特点
- 3、堆区的内存分配与释放
- 4、注意事项:
- 三、全局/静态存储区
- 1、全局存储区
- 1.1 全局变量
- 1.2 静态全局变量
- 2、静态存储区
- 2.1 静态局部变量
- 四、常量存储区
- 1、常量的定义和存储
- 2、常量字符串
前言
在c语言中,内存主要分为4个区域:
栈区、堆区、全局/静态存储区、常量存储区
一、栈区
在c语言中,栈区(Stack)是内存中的一个重要区域,用于存储局部变量、函数参数、返回地址以及函数调用的上下文信息。
1、栈区的特点:
栈区的主要特点就是由系统自动管理其内存分配和释放,遵循后进先出的原则
1.1 自动管理
栈区的内存分配和释放由编译器在编译时和运行时自动处理,程序员无需手动处理。当函数被调用时,会在栈上为其局部变量和参数分配空间;当函数返回时,这些空间会被自动释放掉。
例如:写一个加法函数
#include<stdio.h>
int add(int x, int y)
{int z = x + y;return z;
}
int main()
{int a = 10;int b = 20;int ret = add(a, b);printf("%d\n",ret);return 0;
}
在程序运行时,编译器会给add函数开辟一个内存空间,此时参数部分x,y还有在函数内部创建的变量z都存储在函数的栈区,当main函数运行到下一步打印返回值的时候,此时add函数已经被调用完毕,那么存储在栈区上的函数的变量,参数,返回值等都会随着函数的使用完毕而被自动释放掉还给操作系统。此时就不存在这些变量,也不能对这些变量进行使用,因为这部分空间已经不再给与你使用了,还给操作系统了,使用的时候就没有权限,强制使用就会进行错误报错(没有使用权限)。
1.2 后进先出
栈区的内存分配和释放遵循先进后出的原则。这意味着最后分配的内存最先被释放,最先分配的内存最后被释放。这种特性使得栈非常适用于函数调用与返回的。
其实对于栈区后进先出的这个特性看过函数栈帧的读者们都应该很了解
在函数栈帧中,函数使用的时候都是进行压栈进行存储的。这里小编还是拿上面的加法函数进行说明。
在程序开始运行的时候,都是从main函数进入的,main函数是程序的入口,既然要使用main函数肯定也需要开辟内存空间,所以main函数在函数的最底部进行存放,然后进入main函数内存创建变量a和b继续存放在内存空间,此时存放的位置是由低地址到高地址进行存放,放在main开辟的内存空间的上面,这样一个个变量创建放在上面,就像是把下面的数据进行一直压着一样的。然后当函数运行结束想要取出来的时候,就跟日常去货物仓取货一样,总不能从下面开始拿,那么一下全部都会倒塌,所以从上面开始拿,上面开始拿,那不就是刚存进去的么,也就是后面存进的,所以就是后进先出的原理。
1.3 有限大小
栈区的大小通常由操作系统在程序启动时确定,并且可以在程序运行时通过系统调用进行调整。由于栈区空间的有限,过大的栈区使用(如深度递归和大量的局部变量)可能会导致栈溢出(Stack Overflow),从而导致程序崩溃。
像在这里小编写的一个递归函数一样,在这里递归函数没有停止条件,那么函数就会一直递归下去,不停的调用test函数,不断的为test函数开辟空间,但是栈区是有大小限制的,你不断的栈区上面进行内存使用,达到上限的时候就会超出使用范围,就跟一杯水,已经满了,你还不停的往里面添加,那么水就会溢出来,这和栈溢出是一样的道理的。
1.4 高速访问
栈区通常位于内存的较低地址区域,并且由于栈的线性增长和自动管理特性,使得栈上的数据访问速度非常快。
较低地址区域
栈区通常被分配在内存的较低地址区域。这意味着栈的起始地址较低,随着数据的压入(push),栈顶地址会逐渐向较高的内存地址移动。这种布局有利于快速定位栈顶和栈中的数据。
线性增长
栈是线性增长的,意味着数据只能在一个方向上(通常是向高地址方向)增加或减少。这种简单的增长模式有助于快速管理栈内存,因为不需要复杂的内存分配和释放算法。
自动管理特性
栈的管理是由编译器和运行时系统自动完成的。当函数被调用时,函数的局部变量和参数会被压入栈中;当函数返回时,这些数据会从栈中弹出,并释放相应的内存空间。这种自动管理减少了程序员手动管理内存的需求,同时也减少了内存泄漏和野指针等错误的风险。
数据访问速度快
由于栈区通常位于内存的较低地址区域,并且栈的线性增长和自动管理特性简化了内存访问模式,因此栈上的数据访问速度非常快。此外,栈区通常位于CPU缓存(cache)友好的地址空间内
,这意味着栈上的数据更有可能被缓存,从而进一步提高了访问速度。
1.5 栈区存储方向
栈区的存储方向是向内存地址减小的方向增长,即由高地址向低地址增长。
测试题
不要小看这一道题目,里面涉及了5个知识点:
- 这里main函数内是局部变量,放在栈区。
- 栈区的存储方向是向内存减小的方向增长,即由高地址向低地址增长。
- 数组在内存中的存储是连续的,且由低地址向高地址存储。
- 小端字节序存储:低位字节数据内容存储在内存的低地址出,高位字节数据内容存储在内存的高地址处。
- %x打印为十六进制形式打印,且打印顺序由高位字节数据到低位字节数据。
根据上述5点我们可以画出该数据在内存中的存储
2、栈区使用注意事项
- 避免栈溢出:由于栈区大小有限,程序员应避免使用深度递归或大量局部变量来防止栈溢出。
- 避免栈上分配大数组或结构体:虽然栈区访问速度快,但由于其大小限制,不建议在栈上分配过大的数组或结构体。这些数据结构应优先考虑在堆区(Heap)上分配。
- 注意变量生命周期:由于栈区内存由系统自动管理,程序员应特别注意变量的生命周期。在函数返回后,栈上的局部变量将不再有效,不应被访问。
二、堆区
1、堆区的定义
堆区是c语言中用于动态分配内存的区域,与栈区不同的是,堆区的内存分配和释放是需要程序员自己手动控制的,而不是由系统自动管理的。在上面栈区的内存分配和释放是由编译器编译和运行时自动处理的。
2、堆区的特点
特点1:大小可变
堆区的大小是不固定的,可以根据需要动态地增加和减少。
而关于堆区的内存分配由程序员自己进行分配的话,就需要用到四个函数:malloc,calloc,realloc,free。这四个函数,前面三个函数是用来开辟和调整需要的内存大小,free函数则是释放程序员动态分配的内存。所以在堆区,堆区的大小是不固定的。可以随时根据程序员的需要程序员自己进行调整。
特点2:不连续性
堆区的数据块可以随意的分配和释放,它们的位置是不固定的。
简单的来说,堆区是由程序员自己调用函数进行开辟的,哪里内存满足程序员需要的内存大小都可以进行存储。
特点3:长生命周期
堆区分配的内存空间在程序运行期间一直存在,直到显式地释放。
特点4:手动管理
因为堆区分配的内存空间在程序运行期间一直都存在,那么如果不及时的将它释放他就会一直存放在那里,所以程序员在堆区开辟完内存空间后,不需要使用的时候,需要将它及时的释放。
3、堆区的内存分配与释放
堆区的内存分配
- malloc函数:malloc函数是用于在堆区分配指定大小的内存空间,并且返回一个指向该内存空间的指针。如果分配失败,则返回NULL。
- calloc函数:calloc函数和malloc函数类似,只不过calloc函数在堆区分配指定大小的内存空间的时候,会将分配的内存初始化为0。
- realloc函数:realloc函数用于调整已分配内存的大小。如果新的大小大于原大小,则扩展内存区域;如果新的大小小于原大小,则缩小内存区域并释放多余的内存空间。
堆区的内存释放
free函数:free函数是用于释放之前通过malloc、calloc或者realloc分配的内存。释放后的内存不再被程序使用,直到再次分配。
4、注意事项:
- 避免内存泄漏:
程序员需要确保不再需要堆区内存的时候及时释放它,以避免内存泄漏。内存泄漏会导致程序占用的内存不断增加,最终导致系统崩溃。
- 避免使用野指针
free函数将动态分配的内存释放的时候,应该将指向这块内存的指针置为NULL指针,以避免野指针的出现。野指针是指向已经释放内存的空间,它可能导致程序崩溃或不可预测的行为。
三、全局/静态存储区
1、全局存储区
全局存储区用于存储全局变量和静态全局变量。这些变量在整个程序的整个生命周期内都存在,并且可以在程序的任何地方访问(对于全局变量)或者定义他们文件的内部访问(对于静态局部变量)
1.1 全局变量
- 定义:在函数外部定义的变量
- 作用域:整个程序(所有文件,如果变量被声明为extern)
- 生命周期:从程序开始到程序结束
- 示例:
int a = 10; //a即为全局变量
int main()
{printf("%d",a)return 0;
}
1.2 静态全局变量
- 定义:在函数外部被static关键字定义的变量
- 作用域:定义他们的文件内部(切记只能在它们定义的文件内部,相当于static将它锁死在那个文件中,其他文件中使用会出错)
- 生命周期:从程序开始到结束
- 示例
static int c = 10;
int main()
{printf("%d", c);return 0;
}
2、静态存储区
静态存储区不仅包含静态全局变量,还包含静态局部变量和常量字符串。这些变量在程序的整个生命周期内都存在,但他们的可见性和作用域可能有所不同。
2.1 静态局部变量
- 定义:在函数内部使用static关键字定义的变量
- 作用域:定义它们的函数内部
- 生命周期:从程序开始到程序结束(即使函数执行完毕,变量也不会销毁)。
- 示例:
int test()
{static int i = 0;i++;return i;
}
int main()
{for (int i = 0; i < 5; i++){int ret = test();printf("%d", ret);}return 0;
}
分析
正常来说,像i这种函数变量属于临时变量,存储在栈区,随着函数的使用结束而内存销毁。在这里我们使用static修饰变量i,此时i不再存储在栈区,而是存储在静态存储区,此时i不会随着函数的使用结束而销毁,运行的时候i是逐渐增加的值,而不是进来函数变量就重新开辟一次。这与不用static的结果是截然不同的。
使用static修饰,变量不会随函数使用完毕而销毁
不使用static函数定义的变量i,此时存储在栈区,随着函数的使用结束会销毁,需要使用的时候需要重新创建
四、常量存储区
在c语言中,常量存储区(也称为常量数据段或只读数据段)是内存中的一个特定区域,用于存储程序中的常量值。这些常量值在程序的整个生命周期都不会发生改变,并且通常保存在只读内存中,以防止它们被意外修改。
1、常量的定义和存储
在c语言中,常量可以通过多种方式定义,包括const关键字、#define预处理指令、以及枚举类型
- const关键字:用于声明一个变量为常量,该变量的值在初始化后不能被修改
const int MAX_SIZE = 100;
- #define预处理指令:用于在预处理阶段定义常量。这种方式定义的常量实际上是在编译前进行文本替换,而不是真正的变量。例如
#define PI 3.14159
- 枚举类型(enum):用于定义一组命名的整数常量。例如:
enum Color { RED, GREEN, BLUE };
2、常量字符串
- 定义:常量字符串是用双引号括起来一系列字符,它表示一个
不可变的字符序列
。在c语言中,字符串以空字符(‘\0’)作为结束标志,因此常量字符串在内存中上实际是一个以空字符结尾的字符数组 - 特性:
- 只读性:常量字符串的值在程序运行期间是不可改变的。如果尝试修改常量字符串的内容,将会导致未定义行为,通常是程序崩溃或数据损坏。
- 存储位置:
常量字符串通常存储在程序的只读数据段(也称为常量存储区)
中,这意味着即使程序的其他部分(如堆区或全局\静态存储区)的数据发生改变,常量字符串的内容也不会发生改变。- c语言中,常量字符串是通过字符数组来表示的。然而,与普通的字符数组不同的是,常量字符串的数组元素是不可以被修改的。
- 声明与初始化:
const char *str = "Hello, World!";
这里,str是一个指向常量字符串的指针,而"Hello, World!"则是存储在只读数据段中的常量字符串。
注意事项:
- 当使用指针指向常量字符串时,应该确保不会通过该指针修改字符串的内容。如果确实要修改字符串,应该使用字符数组而不是字符串常量
- 编译器可能会将相同的常量字符串合并为一个单一的存储位置,以节省内存空间。这是编译器优化的一部分,但程序员不应该依赖这种优化来节省内存。
错误示例代码:
#include <stdio.h> int main() { const char *str = "Hello, World!"; printf("%s\n", str); // 尝试修改常量字符串的内容(错误做法) // str[0] = 'h'; // 这将导致未定义行为 return 0;
}
在这个示例中,str是一个指向常量字符串"Hello, World!"的指针。程序通过printf函数打印出这个字符串。然而,如果尝试修改str指向的字符串内容(如注释中所示),将会导致未定义行为。
相关文章:

c语言内存块讲解
文章目录 前言一、栈区1、栈区的特点:1.1 自动管理1.2 后进先出1.3 有限大小1.4 高速访问1.5 栈区存储方向 2、栈区使用注意事项 二、堆区1、堆区的定义2、堆区的特点3、堆区的内存分配与释放4、注意事项: 三、全局/静态存储区1、全局存储区1.1 全局变量…...
2024年10月23日Github流行趋势
项目名称:hiteshchoudhary / apihub 项目维护者:wajeshubham, atulbhatt-system32, jwala-anirudh, arnb-smnta, shrey-dadhaniya 项目介绍:您自己的API Hub,用于学习和掌握API交互。非常适合前端、移动开发人员和后端开发人员。 …...
YOLOv6-4.0部分代码阅读笔记-dbb_transforms.py
dbb_transforms.py yolov6\layers\dbb_transforms.py 目录 dbb_transforms.py 1.所需的库和模块 2.def transI_fusebn(kernel, bn): 3.def transII_addbranch(kernels, biases): 4.def transIII_1x1_kxk(k1, b1, k2, b2, groups): 5.def transIV_depthconcat(kernel…...

C++ 基础语法 一
C 基础语法 一 文章目录 C 基础语法 一const 限定符常量指针类型别名autodecltypeQStringvector迭代器指针和数组显示转换static_castconst_cast 函数尽量使用常量引用数组形参不要返回局部对象的引用和指针返回数组指针 C四种转换内联函数constexpr函数函数指针 const 限定符 …...
B2020 分糖果
题目描述 某个幼儿园里,有 55 位小朋友编号依次为 1,2,3,4,51,2,3,4,5 他们按照自己的编号顺序围坐在一张圆桌旁。他们身上有若干糖果,现在他们玩一个分糖果游戏。从 11 号小朋友开始,将自己的糖果均分成 33 份(如果有多余的糖果…...

VBA字典与数组第二十讲:如何在代码运行时创建数组
《VBA数组与字典方案》教程(10144533)是我推出的第三套教程,目前已经是第二版修订了。这套教程定位于中级,字典是VBA的精华,我要求学员必学。7.1.3.9教程和手册掌握后,可以解决大多数工作中遇到的实际问题。…...

字符串统计(Python)
接收键盘任意录入,分别统计大小写字母、数字及其它字符数量,打印输出。 (笔记模板由python脚本于2024年11月02日 08:23:31创建,本篇笔记适合熟悉python字符串并懂得基本编程技法的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网…...

NVR小程序接入平台/设备EasyNVR多个NVR同时管理视频监控新选择
在数字化转型的浪潮中,视频监控作为安防领域的核心组成部分,正经历着前所未有的技术革新。随着技术的不断进步和应用场景的不断拓展,视频监控系统的兼容性、稳定性以及安全性成为了用户关注的焦点。NVR小程序接入平台/设备EasyNVR,…...

怎样能把图片做压缩处理?学会4款在线工具高效压缩图片
随着现在图片质量不断的提高,导致图片的大小也越来越大,很多的网上平台只能上传比较小的图片,那么可以使用压缩图片或者图片改尺寸的方式来修改图片大小,那么图片压缩的操作技巧是什么样的呢?本文将带大家了解4个操作简…...

ZooKeeper 客户端API操作
文章目录 一、节点信息1、创建节点2、获取子节点并监听节点变化3、判断节点是否存在4、客户端向服务端写入数据写入请求直接发给 Leader 节点写入请求直接发给 follow 节点 二、服务器动态上下线监听1、监听过程2、代码 三、分布式锁1、什么是分布式锁?2、Curator 框架实现分布…...
常用滤波算法(一)-限幅滤波法
文章目录 一、限幅滤波法原理二、C语言实现限幅滤波法三、代码解析定义限制值:限幅滤波函数:模拟获取新数据:主函数: 四、结论 限幅滤波法 限幅滤波法,作为一种简单而有效的滤波方法,通过限制信号的幅值范围…...

江协科技STM32学习- P33 实验-软件I2C读写MPU6050
🚀write in front🚀 🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝…...

BusHound工具的使用-调试USB
12 1.Capture(捕捉按钮)、2.Save(保存按钮)、3.Setting(设置要监听的,输入输出)、4.Device(选择要监听的设备)、5.Help(帮助按钮)、6.Exit(退出按钮)。 一、Capture页面 1.Device 表示是29设备端口,打印机。 2.Phase,各类协议,…...

Hadoop生态圈框架部署(四)- Hadoop完全分布式部署
文章目录 前言一、Hadoop完全分布式部署(手动部署)1. 下载hadoop2. 上传安装包2. 解压hadoop安装包3. 配置hadoop配置文件3.1 虚拟机hadoop1修改hadoop配置文件3.1.1 修改 hadoop-env.sh 配置文件3.3.2 修改 core-site.xml 配置文件3.3.3 修改 hdfs-site…...

Spring Boot 与 Vue 共铸卓越采购管理新平台
作者介绍:✌️大厂全栈码农|毕设实战开发,专注于大学生项目实战开发、讲解和毕业答疑辅导。 🍅获取源码联系方式请查看文末🍅 推荐订阅精彩专栏 👇🏻 避免错过下次更新 Springboot项目精选实战案例 更多项目…...
leetcode3. Longest Substring Without Repeating Characters
Given a string s, find the length of the longest substring without repeating characters. Example 1: Input: s “abcabcbb” Output: 3 Explanation: The answer is “abc”, with the length of 3. Example 2: Input: s “bbbbb” Output: 1 Explanation: The ans…...
Mongodb使用视图连接两个集合
您可以使用 $lookup 为两个集合创建一个视图,然后对该视图运行查询。应用程序可以查询视图,而无需构建或维护复杂的管道。 例子 创建两个样本集合 inventory 和 orders: db.inventory.insertMany( [{ prodId: 100, price: 20, quantity: 1…...

SIP是什么?
SIP(Session Initiation Protocol,会话启动协议)是一个用于建立、更改和终止多媒体会话的应用层控制协议,其中的会话可以是IP电话、多媒体会话或多媒体会议。 SIP是IETF多媒体数据和控制体系结构的核心协议(最新RFC文档…...
Day 39 || 01背包、416. 分割等和子集
01背包 题目链接:卡码网第46题 二维解题思路:需要建立一个i行k列的dp数组,i表示每个物品,k代表容量,初始化数组子一列为0,第一行从背包开始能够放入起始为价值,其他都为0。for双循环先背包后物…...
调用detr-resnet-50进行目标检测
from transformers import DetrImageProcessor, DetrForObjectDetection import torch from PIL import Imageimage = Image.open("1.jpg") torch.set_default_device("cuda"...
python八股文算法:三数之和
双指针解法: 原理见注释 # 2025/6/6 9:40 # -*- coding:UTF-8 -*- nums [-1, 0, 1,1, 2, -1, -4,0,2,1,-3,4,10,-9] def three_sum(nums):nums.sort()n len(nums)result []for i in range(n-2):# n-2,此时i取值到n-2-1,即倒数第3个数&…...

20250603在荣品的PRO-RK3566开发板的Android13下的使用命令行来查看RK3566的温度【显示优化版本】
20250603在荣品的PRO-RK3566开发板的Android13下的使用命令行来查看RK3566的温度【显示优化版本】 2025/6/3 11:58 RK3566的cpu运行效率 top busybox top rk3566_t:/ # rk3566_t:/ # rk3566_t:/ # cd /sys/class/thermal/ rk3566_t:/sys/class/thermal # ls -l rk3566_t:/sys/c…...

[ Qt ] | 与系统相关的操作(三):QFile介绍和使用
目录 之前的操作文件的方式 Qt中的文件操作简介 QFile 打开 读 写 关闭 一个例子来说明 QFileInfo 之前的操作文件的方式 C语言中,fopen 打开文件,fread fwrite 读写文件,fclose 关闭文件。 C中,fstream 打开文件&…...

【笔记】旧版MSYS2 环境中 Rust 升级问题及解决过程
下面是一份针对在旧版 MSYS2(安装在 D 盘)中,基于 Python 3.11 的 Poetry 虚拟环境下升级 Rust 的处理过程笔记(适用于 WIN 系统 SUNA 人工智能代理开源项目部署要求)的记录。 MSYS2 旧版环境中 Rust 升级问题及解决过…...

从理论崩塌到新路径:捷克科学院APL Photonics论文重构涡旋光技术边界
理论预言 vs 实验挑战 光子轨道角动量(Orbital Angular Momentum, OAM)作为光场调控的新维度,曾被理论预言可突破传统拉曼散射的对称性限制——尤其是通过涡旋光(如拉盖尔高斯光束)激发晶体中常规手段无法探测的"…...

Elasticsearch最新入门教程
文章目录 Elasticsearch最新入门教程1.Elasticsearch安装2.Kibana安装3.Elasticsearch关键概念4.SpringBoot整合Elasticsearch4.1 导入Elasticsearch数据4.2 创建SpringBoot项目4.3 修改pom.xml文件4.4 创建es实体类4.5 创建es的查询接口 5.DSL语句5.1 无条件查询5.2 指定返回的…...

【Linux网络篇】:从HTTP到HTTPS协议---加密原理升级与安全机制的全面解析
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨ ✨ 个人主页:余辉zmh–CSDN博客 ✨ 文章所属专栏:Linux篇–CSDN博客 文章目录 HTTPS协议原理一.预备知识1.什么是“加密”2.为什么要“加密”…...

vscode自定义主题语法及流程
vscode c/c 主题 DIY 启用自己的主题(最后步骤) 重启生效 手把手教你制作 在C:\Users\jlh.vscode\extensions下自己创建一个文件夹 里面有两个文件一个文件夹 package.json: {"name":"theme-jlh","displayName":"%displayName%&qu…...

1.1Nodejs和浏览器中的二进制处理
Buffer 在 Node.js 中,Buffer 类用于处理二进制数据。由于 JavaScript 在浏览器环境中主要用于处理字符串和数字等类型的数据,对二进制数据的处理能力较弱,因此 Node.js 引入了 Buffer 类来弥补这一不足,特别是在处理文件系统操作…...

Unity优化篇之DrawCall
当然可以!以下是完整、详尽、可发布的博客文章,专注讲解 Unity 的静态合批与动态合批机制,并详细列出它们对 Shader 的要求和所有限制条件。文章结构清晰、技术深度足够,适合发布在 CSDN、掘金、知乎等技术平台。 urp默认隐藏动态…...