您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 订阅
  捐助
C语言的内存管理机制
 
作者:askunix_hjh
   次浏览      
 2020-7-3 
 
编辑推荐:

本文主要讲解了变量:全局变量,局部变量及静态变量 及内存管理的目的。更多内容请看下文。
文章来自于csdn,由火龙果Anna编辑推荐。

内存资源是非常有限的。尤其对于移动端开发者来说,硬件资源的限制使得其在程序设计中首要考虑的问题就是如何有效地管理内存资源。本文是作者在学习C语言内存管理的过程中做的一个总结。

变量概念:

全局变量(外部变量):出现在代码块{}之外的变量就是全局变量。

局部变量(自动变量):一般情况下,代码块{}内部定义的变量就是自动变量,也可使用auto显示定义。

静态变量:是指内存位置在程序执行期间一直不改变的变量,用关键字static修饰。

代码块内部的静态变量只能被这个代码块内部访问,代码块外部的静态变量只能被定义这个变量的文件访问。

extern关键字:

1、引用同一个文件中的变量;

2、引用另一个文件中的变量;

3、引用另一个文件中的函数。

注意:C语言中函数默认都是全局的,可以使用static关键字将函数声明为静态函数(只能被定义这个函数的文件访问的函数)。

程序执行流程:

代码区:

程序被操作系统加载到内存的时候,所有的可执行代码(程序代码指令、常量字符串等)都加载到代码区,这块内存在程序运行期间是不变的。代码区是平行的,里面装的就是一堆指令,在程序运行期间是不能改变的。函数也是代码的一部分,故函数都被放在代码区,包括main函数。

静态区

静态区存放程序中所有的全局变量和静态变量。

栈区

栈(stack)是一种先进后出的内存结构,所有的自动变量、函数形参都存储在栈中,这个动作由编译器自动完成,我们写程序时不需要考虑。栈区在程序运行期间是可以随时修改的。当一个自动变量超出其作用域时,自动从栈中弹出。

每个线程都有自己专属的栈;

栈的最大尺寸固定,超出则引起栈溢出;

变量离开作用域后栈上的内存会自动释放。

int main(int argc, char* argv[])
{
char array_char[1024*1024*1024] = {0};
array_char[0] = 'a';
printf("%s", array_char);
getchar();
}

 

栈溢出怎么办呢?就该堆出场了。

堆(heap)和栈一样,也是一种在程序运行过程中可以随时修改的内存区域,但没有栈那样先进后出的顺序。更重要的是堆是一个大容器,它的容量要远远大于栈,这可以解决内存溢出困难。一般比较复杂的数据类型都是放在堆中。但是在C语言中,堆内存空间的申请和释放需要手动通过代码来完成。

那堆内存如何使用?

malloc函数用来在堆中分配指定大小的内存,单位为字节(Byte),函数返回void *指针;free负责在堆中释放malloc分配的内存。

#include <stdlib.h>
#include<stdio.h>
#include <string.h>
void print_array(char *p, char n)
{
int i = 0;
for (i = 0; i < n; i++)
{
printf("p[%d] = %d\n", i, p[i]);
}
}
int main(int argc, char* argv[])
{
char *p = (char *)malloc(1024 * 1024 * 1024);//在堆中申请了内存
memset(p, 'a', sizeof(int)* 10);//初始化内存
int i = 0;
for (i = 0; i < 10; i++)
{
p[i] = i + 65;
}
print_array(p, 10);
free(p);//释放申请的堆内存
getchar();
}

这样就解决了刚才栈溢出问题。堆的容量有多大?理论上讲,它可以使用除了系统占用内存空间之外的所有空间。实际上比这要小些,比如我们平时会打开诸如QQ、浏览器之类的软件,但这在一般情况下足够用了。不能将一个栈变量的地址通过函数的返回值返回,如果我们需要返回一个函数内定义的变量的地址该怎么办?可以这样做:

int *getx()
{
int *p = (int *)malloc(sizeof(int));//申请了一个堆空间
return p;
}
int main(int argc, char* argv[])
{
int *pp = getx();
*pp = 10;
free(pp);
return 0;
}
//类似创建链表时,新增一个节点。

可以通过函数返回一个堆地址,但记得一定用通过free函数释放申请的堆内存空间。

分析:

main函数和UpdateCounter为代码的一部分,故存放在代码区

数组a默认为全局变量,故存放在静态区

main函数中的”char *b = NULL”定义了自动变量b(variable),故其存放在栈区

接着malloc向堆申请了部分内存空间,故这段空间在堆区

需要注意以下几点:

栈是从高地址向低地址方向增长;

在C语言中,函数参数的入栈顺序是从右到左,因此UpdateCounter函数的3个参数入栈顺序是a1、c、b;

C语言中形参和实参之间是值传递,UpdateCounter函数里的参数a[1]、c、b与静态区的a[1]、c、b不是同一个;

内存管理的目的

学习内存管理就是为了知道日后怎么样在合适的时候管理我们的内存。那么问题来了?什么时候用堆什么时候用栈呢?一般遵循以下三个原则:

如果明确知道数据占用多少内存,那么数据量较小时用栈,较大时用堆;

如果不知道数据量大小(可能需要占用较大内存),最好用堆(因为这样保险些);

如果需要动态创建数组,则用堆。

创建动态数组:

//动态创建数组
int main()
{
int i;
scanf("%d", &i);
int *array = (int *)malloc(sizeof(int) * i);
//...//这里对动态创建的数组做其他操作
free(array);
return 0;
}

操作系统在管理内存时,最小单位不是字节,而是内存页(32位操作系统的内存页一般是4K)。比如,初次申请1K内存,操作系统会分配1个内存页,也就是4K内存。4K是一个折中的选择,因为:内存页越大,内存浪费越多,但操作系统内存调度效率高,不用频繁分配和释放内存;内存页越小,内存浪费越少,但操作系统内存调度效率低,需要频繁分配和释放内存。

   
次浏览       
相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程
最新活动计划
LLM大模型应用与项目构建 12-26[特惠]
QT应用开发 11-21[线上]
C++高级编程 11-27[北京]
业务建模&领域驱动设计 11-15[北京]
用户研究与用户建模 11-21[北京]
SysML和EA进行系统设计建模 11-28[北京]
 
最新文章
.NET Core 3.0 正式公布:新特性详细解读
.NET Core部署中你不了解的框架依赖与独立部署
C# event线程安全
简析 .NET Core 构成体系
C#技术漫谈之垃圾回收机制(GC)
最新课程
.Net应用开发
C#高级开发技术
.NET 架构设计与调试优化
ASP.NET Core Web 开发
ASP.Net MVC框架原理与应用开发
更多...   
成功案例
航天科工集团子公司 DotNet企业级应用设计与开发
日照港集 .NET Framewor
神华信 .NET单元测试
台达电子 .NET程序设计与开发
神华信息 .NET单元测试
更多...