【零基础学C语言】知识总结十一:动态内存分配!

动态内存分配(动态存储期)
在程序执行并使用该变量的时候分配内存空间,使用完毕立即释放.
动态内存分配就 是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不像数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据 程序的需要即时分配,且分配的大小就是程序要求的大小。
当程序运行到需要一个动态分配的变量或对象时,必须向系统申请取得堆中的一块所需大小的存贮空间,用于存贮该变量或对象。当不再使用该变量或对象时,也就是它的生命结束时,要显式释放它所占用的存贮空间,这样系统就能对该堆空间进行再次分配,做到重复使用有限的资源。
在使用数组的时候,总有一个问题困扰着我们:数组应该有多大?在很多的情况下,你并不能确定要使用多大的数组,比如上例,你可能并不知道我们要定义的这个数组到底有多大,那么你就要把数组定义得足够大。这样,你的程序在运行时就申请了固定大小的你认为足够大的内存空间。

即使你知道你想利用的空间大小,但是如果因为某种特殊原因空间利用的大小有增加或者减少,你又必须重新去修改程序,扩大数组的存储范围。这种分配固定大小的内存分配方法称之为静态内存分配。但是这种内存分配的方法存在比较严重的缺陷,特别是处理某些问题时:在大多数情况下会浪费大量的内存空间,在少数情况下,当你定义的数组不够大时,可能引起下标越界错误,甚至导致严重后果。
我们用动态内存分配就可以解决上面的问题. 所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。
从以上动、静态内存分配比较可以知道动态内存分配相对于静态内存分配的特点:
1、不需要预先分配存储空间;
2、分配的空间可以根据程序的需要扩大或缩小。
常见的动态内存错误:
(1)对NULL指针进行解引用操作
(2)对分配的内存进行操作时越过边界
(3)释放并非动态分配的内存
(4)试图释放一块动态分配的内存的一部分以及一块内存被释放之后被继续使用。
说明:
1、动态分配最常见的错误就是忘记检查所请求的内存是否成功分配。
2、动态内存分配的第二大错误来源是操作内存时超出了分配内存的边界。
当你使用free时,可能出现各种不同的错误:
1、传递给free的指针必须是一个从malloc、calloc或realloc函数返回的指针。
2、传递给free函数一个指针,让它释放一块并非动态分配的内存可能导致程序立即终止或在晚些时候终止。
3、试图释放一块动态分配内存的一部分也有可能引起类似问题。
const 函数(补充)
之前一直把这个关键字漏掉了现在补上,const 限定符,它把一个对象转换成一个常量,C语言中const关键字是constant的缩写,通常翻译为常量、常数等,有些朋友一看到const关键字马上就想到了常量。事实上在C语言中const功能很强大,它可以修饰变量、数组、指针、函数参数等。
1、修饰变量:
在程序中使用const修饰变量,就可以对变量声明为只读特性,并保护变量值以防被修改。如下:
const int i = 5; 变量i具有只读特性,不能够被更改;若想对i重新赋值,如i = 10;则是错误的。
值得注意的是,定义变量的同时,必须初始化。定义形式也可以写成int const i=5,同样正确。
此外,const修饰变量还起到了节约空间的目的,通常编译器并不给普通const只读变量分配空间,而是将它们保存到符号表中,无需读写内存操作,程序执行效率也会提高。
2、修饰数组
C语言中const还可以修饰数组,举例如下:
const int array[5] = {1,2,3,4,5};
array[0] = array[0]+1; //错误
数组元素与变量类似,具有只读属性,不能被更改;一旦更改,如程序将会报错。
3、修饰函数参数
const关键字修饰函数参数,对参数起限定作用,防止其在函数内部被修改。所限定的函数参数可以是普通变量,也可以是指针变量。举例如下:
void fun1(const int i)
i++; //对i的值进行了修改,程序报错
void fun2(const int *p)
(*p)++; //对p指向空间的值进行了修改,程序报错
保护数组中的元素:
为了避免函数的意图不是为了修改数组当中的数据内容,那么在函数原始声明定义中时应使用关键字const,如:
int sum(const a[ ],int n); 这段代码告诉编译器,该函数不能修改a所指向的数组中的内容,如果在函数中不小心使用类似a[i]++;的表达式,那么程序将会报错。
要注意的是,这样使用const并不是要求原数组是常量,而是该函数在处理数组时将其视为常量,不可修改,这样使用const可以保护数组当中的数据不被修改。
4、修饰指针
C语言中const修饰指针要特别注意,共有两种形式,一种是用来限定指向空间的值不能修改;另一种是限定指针不可更改。举例说明如下:
int i = 5;
int j = 6;
int k = 7;
const int * p1 = &i; //定义1
int * const p2 =&j; //定义2
上面定义了两个指针p1和p2。
在定义1中const限定的是 * p1,即其指向空间的值不可改变,若改变其指向空间的值如*p1=20,则程序会报错;但p1的值是可以改变的,对p1重新赋值如p1=&k是没有任何问题的。
在定义2中const限定的是指针p2,若改变p2的值如p2=&k,程序将会报错;但*p2,即其所指向空间的值可以改变,如 * p2=80是没有问题的,程序正常执行。
关于指针赋值和const需要注意一些规则:
1、把const数据或非const数据的地址初始化为指向const的指针或为其赋值是合法的
2、可以声明并初始化一个不能指向别处的指针,关键是const的位置,这时候,这个指针可以修改它所指向的值,但是只能指向初始化时设置的地址。
3、在创建指针时还可以使用两次const,表示该指针既不能修改它所指向的地址,也不能修改它所指向地址上的值
清单:
块
块指的是一块数据,是个抽象的概念,和C语言没有关系,这种抽象的东西,别说其他语言也能用,就是日常生活中也会把东西分块管理,C语言中没有对块进行定义,因为这只是个抽象的概念,块可以是内存块,数据块,程序块,哪怕是豆腐块也能是块... 意思就是在管理中被划分为一类的一个基本单位
存储期:
存储期这也是变量的特点,它称为生存期,表示变量在内存中存在的时间的长短。
1、静态存储期:在程序编译时就分配内存空间并保持不变,程序执行结束后才释放。
2、线程存储期:thread_local,其声明后会给每个线程分配一个单独的私有备份
3、自动存储期:局部变量通常都自动为auto 存储期
4、动态存储期:就是用new 或者malloc分配的内存,如果不主动释放,在整个程序都占有内存
作用域:
这个是表示变量在哪些范围内起作用,由链接点决定。
1、块作用域:用{}括起来的,从声明开始到“}” 结束
2、函数作用域:goto(标识符) 的作用域为整个函数。
3、函数原型作用域:函数声明开始,函数声明结束而结束
4、文件作用域:整个文件或者程序
链接属性:表示变量能在哪些范围内使用.
1、内部链接 :只能在源文件内部使用.
2、外部链接 : 能在源文件内部和外部文件中使用.
3、空连接 : 只能在代码块内(函数内部)使用.
限定符:
volatile:
const:
restrict:
_Atomic:

作者:Mr_Li_
另外的话为了帮助大家,轻松,高效学习C语言/C++,我给大家分享我收集的资源,从最零基础开始的教程到C语言项目案例,帮助大家在学习C语言的道路上披荆斩棘!
微信公众号:C语言编程学习基地
整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)最重要的是你可以在群里面交流提问编程问题哦!

欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!大家也要把握住有限的时光,抓住成长的每一次机会哦~
编程学习视频分享:
