首页 > 编程语言 > 详细

C语言入门学习第二十八天——动态内存管理

时间:2021-03-29 14:27:37      阅读:39      评论:0      收藏:0      [点我收藏+]

镇楼图

技术分享图片

Pixiv:Nardack

〇、一个需要解决的问题

在C99之前无法使用变量作为数组的参数,比如

int n = 5;
int a[n] = {0};
n = 6;

这种做法是不被允许的。

假如你有一个字符串固定只有4字节,无法在运行过程中改变长度,在使用strcat、strcopy等操作还是挺局限的。

于是动态内存管理这种操作就出现啦~

目的就是让你能在运行过程中也可以对内存进行管理

这里只会涉及到stdlib库中的四个函数:malloc、free、calloc、realloc

==============

一、void *malloc ( size )

作用:申请分配size个字节的内存空间

参数:size size_t类型

返回:指向这块内存空间的void指针;调用失败会返回NULL,如果size设置为0也可能会返回NULL(并不意味着调用失败)

int main(){
	int *p = (int*)malloc(sizeof(int));
	if(p == NULL)printf("调用失败\n");
	scanf("%d",p);
    printf("结果为%d\n",*p)
    free(p);
    return 0;
}

借此我们还可以为数组申请内存空间

#include<stdio.h>
#include<stdlib.h> 

int main(){
	int *p = NULL;
	int n;
	printf("定义数组元素:");
	scanf("%d",&n);
	
	p = (int*)malloc(n*sizeof(int));
	
	for(int i = 0;i<n;i++){
		p[i] = i;
	}
	
	for(int i = 0;i<n;i++){
		printf("p[%d] = %d\n",i,p[i]);
	}
	free(p);
	
    return 0;
}

==============

二、void free ( *p );

作用:释放malloc、calloc、realloc或aligned_alloc所申请的堆空间。若参数p为NULL,则不执行任何操作。

参数:p void指针

注意:这个操作并不会修改内存地址,只会释放(释放后会变成非法的空间)

===============

三、void *calloc ( n , size )

我们已经可以使用malloc分配内存空间了,还差初始化这一步,虽然写代码完成。但其实还存在一种更高效的方法,那就是calloc

作用:申请分配n个长度为size的内存空间。( 相当于malloc(n*size) )然后这些内存空间全部被初始化为0

参数:n size_t类型

? size size_t类型

int *a = (int*)calloc(12,sizeof(int));
for(int i = 0;i<12;i++){
	a[i] += i;
    printf("a[%d] = %d\n",i,a[i]);
}
free(a);
//可以看出calloc很好地初始化了内存空间,不然肯定都是随机值

===============

四、void *realloc ( *p , size )

这个函数可以重新分配内存。

比如你使用一个长度为10的字符串,突然不够用了;这时候,realloc可以使字符串在程序运行中进行扩展。(也就是可以实现变长数组)

作用:修改p所指向的内存空间的大小。若新分配的大于原来分配的,原来内存空间中的数据不会被改变;若新分配的小于原来分配的,原来内存空间中的数据可能会丢失

参数:

p

void指针(如果不是NULL,必须是malloc、calloc、realloc所调用的)

size

size_t类型

//这一般会根据用户的要求来拓宽内存空间
//下面为实现【变长数组】的代码
int num;
int count = 0;
int*p = NULL;

do{
    printf("输入整数(输入负数视为放弃输入):");
    scanf("%d",&num);
    count++;

    p = (int*)realloc(p,count*sizeof(int));
    assert(p != NULL);
    p[count-1] = num;
}while(num >= 0);

printf("==============\n");
for(int i = 0;i < count-1;i++){
    printf("p[%d] = %d\n",i,p[i]);
}
技术分享图片

一些诡异的做法:

①p设置为NULL,相当于使用malloc( size )

②size设置为0,相当于使用free( p )

==============

五、内存泄漏Memory Leak

尝试用虚拟机运行一下以下代码体验一下内存泄漏的感觉

注:用虚拟机,用虚拟机~用虚拟机!

卡崩了别怪我没提醒

#include<stdlib.h>

int main(){
	while(1){
		malloc(1);
    }
}

内存泄漏是指已动态分配的堆内存因为某种原因无法释放,造成内存的浪费。轻则占用内存减慢速度重则系统奔溃(数据丢失)

内存泄漏一般比较难发现,最好在敲一些涉及到内存的代码时考虑这个问题

分类

①隐式内存泄漏

没有及时地释放内存而导致内存的堆积

②丢失内存地址

内存地址如果无法获取,就意味着无法对这块内存地址无法进行操作,自然也不可能尝试去free掉。

int *p = (int*)malloc(3);
int q = 3;
p = &q;
/*这个时候会丢失malloc申请的内存地址
也无法进行free操作来释放掉

===============

六、C语言内存布局规律

技术分享图片

地址从低到高分为:

代码段→

数据段→

BSS段→

堆→

未使用的内存空间→

栈→

命令行参数和环境变量

①代码段Text segment

存放程序执行代码的一块内存区域。

这块内存在程序运行前就已经确定,且这块内存通常属于只读

②数据段Initialized data segment

用来存放已初始化的全局变量、局部静态变量

③BSS段 Block Started by Symbol / Unitialized data segment

用来存放未初始化的全局变量

这块内存的数据在程序运行前会被自动初始化为0

④堆Heap

用来存放进程中被动态分配的内存段。

内存大小不固定,可动态扩展、缩小。

使用malloc就是将新分配内存添加到堆上

使用free就是将堆上的内存移走

⑤栈Stack

函数执行的内存区域,一般和堆共享一个区域。

目前对于堆栈的原理没必要详细地掌握,在数据结构中你将会对堆栈有着更深刻的理解

===============

七、堆与栈

手动申请手动释放 自动申请自动释放
速度慢空间大 速度快空间较小
生存周期是从申请到释放 生存周期是从函数调用开始到调用结束
不同函数之间可自由访问 不同函数的局部变量不能互相访问
先进先出 先进后出
是向高地址扩展的 是向低地址扩展的

===============

总结

现在你已经可以运用这四个函数来动态管理内存,当然这些基本操作是为后面一些内容作铺垫的。

如果还想实现更多的对内存的操作可以使用

string.h模块

参考资料

https://fishc.com.cn

http://www.cplusplus.com/reference

https://www.icourse163.org/course/ZJU-200001

https://www.cnblogs.com/Binhua-Liu/archive/2010/08/24/1803095.html

C语言入门学习第二十八天——动态内存管理

原文:https://www.cnblogs.com/AlienfronNova/p/14591875.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!