零基础学C语言知识总结十一动态内存分配
动态内存分配(动态存储期)
在程序执行并使用该变量的时候分配内存空间,使用完毕立即释放。
动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不像数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。
当程序运行到需要一个动态分配的变量或对象时,必须向系统申请取得堆中的一块所需大小的存贮空间,用于存贮该变量或对象。当不再使用该变量或对象时,也就是它的生命结束时,要显式释放它所占用的存贮空间,这样系统就能对该堆空间进行再次分配,做到重复使用有限的资源。
在使用数组的时候,总有一个问题困扰着我们:数组应该有多大?在很多的情况下,你并不能确定要使用多大的数组,比如上例,你可能并不知道我们要定义的这个数组到底有多大,那么你就要把数组定义得足够大。这样,你的程序在运行时就申请了固定大小的你认为足够大的内存空间。
即使你知道你想利用的空间大小,但是如果因为某种特殊原因空间利用的大小有增加或者减少,你又必须重新去修改程序,扩大数组的存储范围。这种分配固定大小的内存分配方法称之为静态内存分配。但是这种内存分配的方法存在比较严重的缺陷,特别是处理某些问题时:在大多数情况下会浪费大量的内存空间,在少数情况下,当你定义的数组不够大时,可能引起下标越界错误,甚至导致严重后果。
我们用动态内存分配就可以解决上面的问题。所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。
从以上动、静态内存分配比较可以知道动态内存分配相对于静态内存分配的特点:
1、不需要预先分配存储空间;
2、分配的空间可以根据程序的需要扩大或缩小。
常见的动态内存错误:
(1)对NULL指针进行解引用操作
(2)对分配的内存进行操作时越过边界
(3)释放并非动态分配的内存
(4)试图释放一块动态分配的内存的一部分以及一块内存被释放之后被继续使用。
说明:
1、动态分配最常见的错误就是忘记检查所请求的内存是否成功分配。
2、动态内存分配的第二大错误来源是操作内存时超出了分配内存的边界。
当你使用free时,可能出现各种不同的错误:
1、传递给free的指针必须是一个从malloc、calloc或realloc函数返回的指针。
2、传递给free函数一个指针,让它释放一块并非动态分配的内存可能导致程序立即终止或在晚些时候终止。
3、试图释放一块动态分配内存的一部分也有可能引起类似问题。实例:动态内存分配实现可变长一维数组
defineGRTSECURENOWARNNGS
includestdio。h
includestdlib。h
includearray。h这个头文件里边包含一个结构表示数组和下列函数的声明原型
constBlocksize20;一次增容20个存储空间
Arrayarraycreat(intintssize);创建一个数组
voidarrayfree(Arraya);回收空间
intarraysize(constArraya);查看当前数组大小
intarrayat(Arraya,intindex);访问数组
voidarrayinlate(Arraya,intmoresize);增容
intmain(void){
Arraya;表示数组初始值的大小
inti,j,n,m0;
while(1){
printf(请输入你需要多大的数组:);
scanf(d,n);
aarraycreat(n);这个可得到a里边返回的参数
printf(输入数据);
for(i0;in;i){
scanf(d,j);
arrayat(a,i)j;这个函数相当与是数组把j的值保存到数组里边的元素中去
}
printf(输出数据:);
for(i0;in;i){遍历输出
printf(d,a。arrray〔i〕);
printf();
}
printf();
printf(输入1初始化数组大小,输入其他表示退出程序:);
scanf(d,n);
if(n1){
m0;清零
j0;
arrayfree(a);释放之前的内存
}
else{
exit(0);退出程序
}
}
return0;
}
Arrayarraycreat(intintssize)创建一个数组
{
Arraya;定义一个数组的结构体
a。sizeintssize;表示数组的长度
a。arrray(int)malloc(sizeof(int)a。size);前一个int是强制类型转换,后面的表示一个int是4个字节总共就是长度乘以
returna;返回的作用是让主函数调用它时,能够得到它的参数
}
voidarrayfree(Arraya)回收空间
{
free(aarrray);
aarrrayNULL;让指针指空不至于成为野指针
asize0;
}
封装
intarraysize(constArraya)查看当前数组大小
{
returnasize;
}
intarrayat(Arraya,intindex)访问数组
{
if(indexasize){
下面的公式是为了算出Blocksize的底在哪
比如130,如果直接加20要加两次,但是用公式就一次完成
arrayinlate(a,(indexBlocksize1)Blocksizeasize);在原来的基础上加20个
}
返回指针加括号是为了保持优先级不出错
return(aarrray〔index〕);如果返回的是值,那将不能被改变,返回指针就可以进行操作了
}
voidarrayinlate(Arraya,intmoresize)增容
{
intp(int)malloc(sizeof(int)(asizemoresize));重新申请一块更大的内存10020
inti;
for(i0;iasize;i){把之前数组的内容拷贝到新的数组中去
p〔i〕aarrray〔i〕;
}
free(aarrray);把之前的数组释放
aarrrayp;将指针改变指向重定向
asizemoresize;大小加上新增的
}
程序演示:
请输入你需要多大的数组:
5hr输入数据
12345
输出数据:
1hr2hr3hr4hr5hr输入1初始化数组大小,输入其他表述退出程序:
1hr请输入你需要多大的数组:
6hr输入数据
123456
输出数据:
1hr2hr3hr4hr5hr6hr输入1初始化数组大小,输入其他表述退出程序:
0hr进程1520已退出。返回代码为0。
按任意键关闭此窗口。。。实例:动态内存分配实现可变长二维数组
includestdio。h
includemalloc。h
intmain(void)
{
intn,m;
scanf(dd,n,m);n5m2按照自己输入来确定二维数组的大小
intp(int)malloc(sizeof(int)n);利用二级指针申请五行元素
p是一个二级指针malloc函数返回一个int的类型sizeof(int)表示乘以的指针类型的大小
、申请m个能够能够存放int类型的空间,并将首地址返回给一个二维指针p;
内存可能的分布情况:
intaint;intp;
intbint;
intcint;
intdint;
(int)一个表示强制类型转换,另一个表示指针int
sizeof(int),不能少,一个指针的内存大小,每个元素是一个指针。用指针长度乘以数量(int)n
这个p指针的数据类型是个二级指针,它指向的这个空间里放的是些一级指针
for(inti0;i5;i)每行有两列元素
{
p〔i〕(int)malloc(sizeof(int)m);每个元素是int大小4m将元素保存到每一行
每一个一级指针值的大小指向一个实际大小的空间
(pi)p〔i〕每一次移动表示行的移动
}
赋值
for(inti0;in;i)
{
for(intj0;jm;j)
{
p〔i〕〔j〕1;
((pi)j)p〔i〕〔j〕
}
}
for(inti0;in;i)
{
for(intj0;jm;j)
{
输出数组每个元素值和地址
printf(dp,p〔i〕〔j〕,p〔i〕〔j〕);
}
printf();
}
for(inti0;in;i){按行释放指针
free(p〔i〕);
}
free(p);释放整体
return0;
}
程序演示:
52
1010F44C01010F44C4
1010F43781010F433C
1010F43301010F4374
1010FAB601010FAB64
1010FAD981010FAB94
进程8432已退出。返回代码为0。
按任意键关闭此窗口。。。
const函数(补充)
之前一直把这个关键字漏掉了现在补上,const限定符,它把一个对象转换成一个常量,C语言中const关键字是constant的缩写,通常翻译为常量、常数等,有些朋友一看到const关键字马上就想到了常量。事实上在C语言中const功能很强大,它可以修饰变量、数组、指针、函数参数等。
1、修饰变量:
在程序中使用const修饰变量,就可以对变量声明为只读特性,并保护变量值以防被修改。如下:
constinti5;变量i具有只读特性,不能够被更改;若想对i重新赋值,如i10;则是错误的。
值得注意的是,定义变量的同时,必须初始化。定义形式也可以写成intconsti5,同样正确。
此外,const修饰变量还起到了节约空间的目的,通常编译器并不给普通const只读变量分配空间,而是将它们保存到符号表中,无需读写内存操作,程序执行效率也会提高。
2、修饰数组
C语言中const还可以修饰数组,举例如下:
constintarray〔5〕{1,2,3,4,5};
array〔0〕array〔0〕1;错误
数组元素与变量类似,具有只读属性,不能被更改;一旦更改,如程序将会报错。
3、修饰函数参数
const关键字修饰函数参数,对参数起限定作用,防止其在函数内部被修改。所限定的函数参数可以是普通变量,也可以是指针变量。举例如下:
voidfun1(constinti)
i;对i的值进行了修改,程序报错
voidfun2(constintp)
(p);对p指向空间的值进行了修改,程序报错
保护数组中的元素:
为了避免函数的意图不是为了修改数组当中的数据内容,那么在函数原始声明定义中时应使用关键字const,如:
intsum(consta〔〕,intn);这段代码告诉编译器,该函数不能修改a所指向的数组中的内容,如果在函数中不小心使用类似a〔i〕;的表达式,那么程序将会报错。
要注意的是,这样使用const并不是要求原数组是常量,而是该函数在处理数组时将其视为常量,不可修改,这样使用const可以保护数组当中的数据不被修改。
4、修饰指针
C语言中const修饰指针要特别注意,共有两种形式,一种是用来限定指向空间的值不能修改;另一种是限定指针不可更改。举例说明如下:
inti5;
intj6;
intk7;
constintp1i;定义1
intconstp2j;定义2
上面定义了两个指针p1和p2。
在定义1中const限定的是p1,即其指向空间的值不可改变,若改变其指向空间的值如p120,则程序会报错;但p1的值是可以改变的,对p1重新赋值如p1k是没有任何问题的。
在定义2中const限定的是指针p2,若改变p2的值如p2k,程序将会报错;但p2,即其所指向空间的值可以改变,如p280是没有问题的,程序正常执行。
关于指针赋值和const需要注意一些规则:
1、把const数据或非const数据的地址初始化为指向const的指针或为其赋值是合法的
2、可以声明并初始化一个不能指向别处的指针,关键是const的位置,这时候,这个指针可以修改它所指向的值,但是只能指向初始化时设置的地址。
3、在创建指针时还可以使用两次const,表示该指针既不能修改它所指向的地址,也不能修改它所指向地址上的值
清单:inta〔10〕;
constdoubleb〔10〕;
constdoublepa;有效
pb;有效
pa〔3〕;有效
inta〔10〕;
constdoubleb〔10〕;
只能将非const数据的地址赋给普通指针(否则,通过指针就能修改const数组中的值了)
doublepa有效
pb;无效
pa〔3〕;有效
voidsum(constdoublea,intn);
此函数可以接受普通数组和const数组名作为参数,因为这两种参数都可以用来初始化指向const的指针
inta〔10〕;
constdoubleb〔10〕;
sum(a,5);合法
sum(b,4);合法
inta〔10〕;
doubleconstpa;p指向数组的开始
pa〔0〕;不允许,因为该指针不能指向别处
p9。9;可以做,更改a〔0〕的值
inta〔10〕;
constdoubleconstpa;
pa〔0〕;不允许
p9。9;不允许
块
块指的是一块数据,是个抽象的概念,和C语言没有关系,这种抽象的东西,别说其他语言也能用,就是日常生活中也会把东西分块管理,C语言中没有对块进行定义,因为这只是个抽象的概念,块可以是内存块,数据块,程序块,哪怕是豆腐块也能是块。。。意思就是在管理中被划分为一类的一个基本单位
存储期:
存储期这也是变量的特点,它称为生存期,表示变量在内存中存在的时间的长短。
1、静态存储期:在程序编译时就分配内存空间并保持不变,程序执行结束后才释放。
2、线程存储期:threadlocal,其声明后会给每个线程分配一个单独的私有备份
3、自动存储期:局部变量通常都自动为auto存储期
4、动态存储期:就是用new或者malloc分配的内存,如果不主动释放,在整个程序都占有内存
作用域:
这个是表示变量在哪些范围内起作用,由链接点决定。
1、块作用域:用{}括起来的,从声明开始到}结束
2、函数作用域:goto(标识符)的作用域为整个函数。
3、函数原型作用域:函数声明开始,函数声明结束而结束
4、文件作用域:整个文件或者程序
链接属性:表示变量能在哪些范围内使用。
1、内部链接:只能在源文件内部使用。
2、外部链接:能在源文件内部和外部文件中使用。
3、空连接:只能在代码块内(函数内部)使用。
限定符:
volatile:
const:
restrict:
Atomic:
作者:MrLi
另外的话为了帮助大家,轻松,高效学习C语言C,我给大家分享我收集的资源,从最零基础开始的教程到C语言项目案例,帮助大家在学习C语言的道路上披荆斩棘!可以来我粉丝群领取哦
编程学习书籍分享:
编程学习视频分享:
整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)最重要的是你可以在群里面交流提问编程问题哦!
对于CC感兴趣可以关注小编在后台私信我:【编程交流】一起来学习哦!可以领取一些CC的项目学习视频资料哦!已经设置好了关键词自动回复,自动领取就好了!
定制化智慧食堂模式,让就餐更简单随着社会的快速发展,团餐企业也愈加重视对数字化平台的搭建与发展,以求团餐行业快速健康发展,因此,将团餐智慧化是一种新手段,它能够推动点单灵活性菜品质量以及效率方面的改进,拓展了团餐
增程式电动汽车貌似成为一种新的选择,燃油车离淘汰还远吗?前有理想ONE连续霸榜中大型SUV汽车排行榜第一名,并且凭借理想one完胜大众一系列车型(途昂途锐揽境ID。6XID。6CROZZ)。后有余大嘴在问界M7发布会强调,增程式是目前最
我在腾冲的秋天里等你秋日生活打卡季秋风拂起那山,那水,那城被熏得微黄阳光透过一缕缕金光此时的腾冲多了一份如梦意蕴趁阳光正好,不妨一起去秋游01hr腾冲赏秋计划之自然之秋腾冲,模糊了四季年均19的气候,
图说信阳换个角度看平桥老家的记忆与乡愁大河网讯在信阳市平桥区,流连过春夏的十里桃花林百里油菜花海千亩荷花池,转眼间,天光云影徘徊在天边映照在大地,顿感天地辽阔山河壮美,一幅秋季绝美的画卷便展现在你我眼前。遥望天目山小金
秋天了,还有很多姑娘搭配高跟凉鞋,是真的不怕冷啊好多人觉得凉鞋是夏季才穿的鞋子,其实到了秋天,也有不少人搭配凉鞋出门,她们会选择高跟款式,衬托身材,也显得很个性,这位美女选择一字带高跟凉鞋,12厘米的鞋跟,字母的设计,一字带的风
致态xJDG蓄势待发!新兴崛起与赛场黑马,等待迎战恭喜JDG在四分之一决赛阶段的角逐中,在BO5的比赛中以30的战绩轻松击败REG杀入4强,能拿到今天这个成绩真的太不容易了!在决胜局中,比赛前期佛耶戈试图闪现绕视野抓下路,但被眼位
闭口粉刺是一种基因病!命里带痘?身边有的小姐妹皮肤好到爆,也有一些倒霉蛋,注意饮食注意睡眠,可还是防不住长痘!长痘原因有研究表明,60的肌肤问题都和遗传有关系,先天基因糖代谢能力弱,就容易导致皮肤暗沉出油。油脂过
心若躁,浮生皆乱心若静,举世皆安有句话说得好心若躁,浮生皆乱心若静,举世皆安。人生过半,就会发现,安静才是立足世间最好的方式。01万千繁华,终归寂静老子说归根曰静,静曰复命。意思是说根是万物生命的来源,回归根才是
科普动起来视频早产宝宝的喂养0000视频加载中编者按全球每年出生的早产宝宝高达1500多万,我国早产儿发生率在10左右。母乳能够为早产宝宝提供最理想的免疫防御和免疫调节,其营养价值和生物学功能也更适合早产儿生
老公,我好像得了抑郁症了在过去的一周,是两个孩子因为疫情停课的一周,对于我来说也是艰难的一周。急性支气管炎没有完全好,两个孩子也都感冒咳嗽了,突然的疫情,突然地停课,让原本身体不舒服的我倍感煎熬。家里没有
得了前列腺炎不知该怎么办?不要慌,教你6种自我疗法1停止熬夜如果患上了前列腺炎,首先要注意的就是熬夜问题。现在社会压力非常大,很多人熬夜已经成了固定的时间。但是前列腺炎患者到了晚上一定要及时休息,不可以再继续熬夜。2健康饮食可以多