C语言:动态内存分配上(C语言篇)

来源:互联网 发布:手机相片分类软件 编辑:程序博客网 时间:2024/05/21 14:51

环境:windows

语言:C语言

动态相对的的名词就是静态,那我们先来讨论一下静态内存的使用及其优缺点。

静态内存:静态的内存使用的是栈空间内存,不用程序员自己来分配。因为静态变量占用的存储空间对于编译器而言是可预计的,静态内存只需要编程的时候直接声明就可以了。且在开辟空间是就已经确定了所需要开辟的空间大小。

程序举例:
#include<stdio.h>
#include<stdlib.h>

int main()
{
int arr[10]={0};
system("pause");
return 0;
}
此时arr就是一块静态内存,由程序运行时自行开辟,开辟于栈,出栈时自行销毁,其大小为40字节,由十个整形数组元素组成。

静态数组的优缺点:
优点:简单,易操作,风险小,便于管理,无内存泄露风险
缺点:限制性大,在空间使用时需要提前预知所需的空间大小,或做出预判,可能带来空间浪费,也可能所开辟的空间不足以解决所需要保存的数据

权衡利弊所以引出了动态内存分配的概念:


动态内存:动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。

内存的静态分配和动态分配的主要区别:
一是时间不同。静态分配发生在程序编译和连接的时候。动态分配则发生在程序调入和执行的时候。
二是空间不同。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由函数malloc进行分配。不过栈的动态分配和堆不同,他的动态分配是由编译器进行释放,无需我们手工实现。
在C语言中谈到动态内存分配受想到的应该就是关键字——malloc和free。malloc用于在内存池中提取一段合适的,由程序猿定义大小的,有指向的内存,且未初始化;free函数将malloc申请的这段空间归还给内存池。
动态内存分配管理的四个关键字:malloc、free、calloc、realloc:(stdlibh文件中)

malloc:(void*)malloc(size_t size);
malloc从内存池中提取一段合适的内存,并向该程序返回一个只想这块内 存的指针,且这块内存并没有进行初始化,如果初始化对该内存的使用非常重要,可以使用关键字memset(string.h文件中),或者使用calloc进行开辟。malloc申请的内存是连续的,以字节为单位的内存。
int *pi;
pi=malloc(100);
这个例子中,malloc像内存池中申请了100个字节大小的内存,并返回指针,由pi带领我们进行访问这段内存。ps:因为编译器版本的不同,malloc实际申请的空间是不相同的,malloc在各个平台下申请的空间可能刚好与你的预期相同,也可能多于你的预期。
malloc申请空间可能失败,失败时返回NULL指针。当内存池为空,或者内存池无法满足你申请的空间(malloc向操作系统在内存池申请的空间是连续的,最差的情况申请2字节空间也会失败),因此为减少错误,对每一个从malloc申请的空间返回的指针进行检查是必要的,确保返回值不是NULL在进行使用,也体现了程序猿的严谨性。

calloc(): (void*)calloc(size_t num_elements,size_t element_size);
在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。calloc在功能上与malloc相同,唯一的区别在于calloc知道分配的空间应该按大小分配,并进行初始化,赋值为0;calloc第一个参数为分配多少个空间,第二个参数为每个空间的大小,总共开辟num_elements*element_size个字节的空间。
calloc开辟的空间,调用free函数进行释放,归还内存池。

realloc(); (void*)realloc(void* ptr,size_t new_size);
realloc函数在动态内存方面是一个比较神奇的函数。realloc开辟的空间可建立在原来空间之上,new_size的大小,可以大于原来的空间,也可以小于原来的空间,当realloc开辟的空间小于原来的空间是,原来空间存储的数据,在新大小空间之后的数据会丢失,归还与内存池。realloc开辟的空间如果大于原来的空间,分三种情况:1、原来空间后面的空间够realloc开辟的空间减去原空间的大小,则直接开辟2、原来空间后面的空间不够新的空间,则realloc会在内存池中重新开辟一块空间,返回一个新指针,并将原来空间上的内容保存在新空间3、内存池的空间不够新开辟空间,即内存开辟失败情况,返回NULL指针。
realloc开辟的空间有free进行释放,归还内存池。

free: (void )free( void *memblock );
free用于释放由malloc、realloc、calloc申请的空间,参数必须是malloc、realloc、calloc返回的值,或者是NULL。参数为NULL时没有任何作用,参数为返回值时,将其代表的空间置为随机值。所以在严谨性方面上,大多数程序员会习惯性的将释放以后的空间置为NULL。

malloc、realloc、calloc、free,

(void*)malloc(size_t size)、(void*)realloc(void* ptr,size_t new_size)、(void*)calloc(size_t n,size_t size)、(void*)free(void* memblock)

返回指针,失败返回NULL、返回指针,失败返回NULL、返回指针,失败返回NULL、指向空间置随机数内存池,

堆空间内存池,堆空间内存池,堆空间将内存返回堆空间size字节大小的连续空间size大小的连续空间,用于对原空间

大小的更改,可大可小n个size大小的(n*size)字节
的连续的空间指针指向的所有空间全部置
随机数,释放空间
在使用动态内存上的好习惯:
1、谁申请的空间,谁保存
2、谁申请的空间,谁释放
3、申请的空间,在使用完后归还给内存池,切记使用完,再调用free函数
4、保存申请空间地址的指针,尽量1个指针代表1段空间,在后面释放空间是相互不影响
多次free内存:
重复free,就会使自由空间遭到破坏,造成程序崩溃。
内存泄漏
A、什么是内存泄漏:
指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
B、对于C和C++这种没有Garbage Collection 的语言来讲,我们主要关注两种类型的内存泄漏:
堆内存泄漏(Heap leak)。对内存指的是程序运行中根据需要分配通过malloc,realloc new等从堆中分配的一块内存,再是完成后必须通过调用对应的 free或者delete 删掉。如果程序的设计的错误导致这部分内存没有被释放,那么此后这块内存将不会被使用,就会产生Heap Leak.
系统资源泄露(Resource Leak).主要指程序使用系统分配的资源比如 Bitmap,handle ,SOCKET等没有使用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。
C、内存溢出
所谓内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是会产生内存溢出的问题。
常见的溢出主要有:
· 内存分配未成功,却使用了它。
常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p 是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc 或new 来申请内存,应该用if(p==NULL)或if(p!=NULL)进行防错处理。
·内存分配虽然成功,但是尚未初始化就引用它。
·内存分配成功并且已经初始化,但操作越过了内存的边界。
例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for 循环语句中,循环次数很容易搞错,导致数组操作越界。
使用free 或delete 释放了内存后,没有将指针设置为NULL。导致产生“野指针”。
常见的动态内存使用问题:
程序1:
#include<stdio.h>
#define BUFFER_SIZE 256
void GetMemory(char **ppszBuf)
{
if(NULL == ppszBuf)
{
assert(0);
return ;
}
ppszBuf = (char *)malloc(BUFFER_SIZE);
return ;
}
void TEest()
{
char *pszBuf = NULL:
GetMemory(&pszBuf);
strcpy(pszBuf , " hello world\n ");
printf("%s" , pszBuf);
free(pszBuf);
return ;
}
分析:
首先我们看到了malloc,接着我们又看到了free,而且两个指针的名字也一样,是不是在心里已经建立了这个动态内存的使用是正确的挂念了,内存泄漏的想法已经没有了。如果是这样的话,那你就错了。这道题的错误并不在没有判断内存申请成功什么的,而是内存泄露问题。
*ppszBuf=(char *)malloc(BUFFER_SIZE);
free(*ppszBuf);
在GetMemory函数申请空间以后,函数结束,与此同时GetMemory函数的栈帧结构被销毁,ppszBuf指针并没有拿到malloc申请的空间,此时便造成了内存泄露,而且free函数释放的并不是空间,因此程序在运行时就会崩溃。而且·strcpy函数也会将字符串复制到一段不存在的空间。所以不论怎么解释这个程序存在的问题都无法被得到演示。而这个也是日后编程需要注意的一点。
程序2:
#include<stdio.h>
void Test()
{
char *p;
char *ptr;
p=malloc(20);
ptr=p;
free(p);
strcpy(ptr, " hello world\n ");
}
分析:
这一个程序只存在一个问题,那就是使用了一段不应该被使用的内存。在这个简单的程序中,大家很容易看出这个错误,可是如果在释放内存以后的几十行,甚至几百行代码,i还能意识到这一点吗?所以我要讲的汗是一个习惯性问题,那就是,malloc所申请的空间,用自己的指针保存,记住,如果实在没有办法,欲要赋值的话,那也尽量使用编号,这样你就会意识到他们是一个家族的,香火断了,他们家族还会存在吗?就是这个个道理,但凡被销毁,其他的,代表同一段空间的指针,自然都不可以使用了。

好了我的动态内存就讲到这里,谢谢你的观看,谢谢!!!

排版可能产生部分问题,请点击链接:点击打开链接

【图片来源】:百度图库
【资料参考】:《C和指针》、《C程序设计》、CSDN博客、百度百科
0 0