C语言学习笔记
堆区空间申请(一)
栈区变量分析
- 所有程序/软件的运行,都是由操作系统统一调配的,操作系统是程序的运行环境
- 运行中的多个程序之间,内存是不交叉的
- 程序结束后,操作系统还要释放其使用的资源,来给其他程序使用
- 申请的空间种分为五个区域栈区(堆栈区),堆区,全局区,字符常量区,代码区,我们之前讲的这些定义变量、数组都是在内存的栈区存储。
- 为什么分这么多区域?在实际生活中,这和公司差不多,部门分的细致,工作分发的就有针对性,效率就会高。
- 由谁申请?
栈区的特点:内存由系统申请,在变量生命周期结束时由系统释放,也就是说,在程序运行的时候,系统要多个任务,就是检测变量是否该释放了,简单来说,就是cpu要抽时间去执行这部分功能。所以,如果这种变量比较多,不加节制的定义的话,那CPU的额外的工作量就会加大,综合下来,程序的运行效率就会低下。
堆区的特点:由我们程序员随时申请,由我们自己随时释放。 - 实际应用中,系统会限制我们可使用的栈区空间大小(默认1M)
stack overflow
即为爆栈。
这个大小可以从编译器的属性中修改,但尽量不修改,不够用了又不得不用再去修改。
如果我们申请的空间,不由操作系统管理,而是我们自己申请与释放,那么就不会占用系统的额外资源了,这样我们就可以申请任意大小的空间了。
malloc函数介绍
- 功能:在堆区空间中申请指定大小的,连续的,一段空间,并返回该空间的首地址。
理论上,32位系统最大申请4G,64位系统最大16Tb(由安装内存决定) - 函数的原型:
void *malloc(size_t size);
int *p = (int*)malloc(4);
这样的一条语句即为申请一个4字节的int类型的堆区空间,并且定义一个指针变量p指向这个空间的首字节的地址(其中的4
当然可以换成sizeof(int)
),如果输入的是小数,也会被系统转换为整数,所以还是输入整数
其实这里的4是int型的,更加标准的写法是4u,但是编译器会自动将有符号整形转换为无符号整型。- 头文件:malloc也是一个内建函数,所以头文件要求并没有那么高,但是更加标准的是
<stdlib.h>
和<malloc.h>
size_t详解
- size_t在不同操作系统的软件里代表的被重命名的函数不一样,实测在32位的编译器中
size_t == unsigned int
,在64位的编译器中size_t == long unsigned int
经过输出字节数可以证明(结果分别是4,8)
malloc申请空间的讨论
- malloc(正常数)时会出现的内存碎片:
在malloc申请了一段指定字节数的空间后,即使后来将其释放,也不一定会被系统再次利用到。例如先申请了20B,后来要申请30B,则会形成内存碎片 - malloc(0):
会申请一个可用空间,但是其不可用 - malloc(极限空间):
申请的空间超出了内存,会得到返回值NULL,即为0;int *p = (int*)malloc(2*1024*1024*1024);
(8G,32位程序中)会提示数据类型溢出;int *p = 2*1024*1024*1024 - 1
甚至会提示两个错误,因为再乘法运算之后,数据类型就已溢出,再-1当然还在继续溢出;
但是如果修改数据类型int *p = (int*)malloc(2u*1024u*1024u*1024u)
就不会再有警告了,因为无符号的整形的范围是0~2^32-1。所以经常会在申请堆区空间之后进行一个if的判断,是否NULL == p,然后提示操作人员能否继续操作。
malloc空间赋值###
- 强制类型转换:
(类型*),malloc就是返回的void*,所以我们要转换成我们想要的类型,操作 - 申请的空间不能初始化,不能像基本数据类型一样直接赋值,例如
int a = 1
,如果直接输出,会得到一个随机数,要是想给*p赋值,我们就要用*p = 0
类似的语句来进行赋值
那如果我们申请的堆区空间是这样的int *p = (int*)malloc(40)
,其实我们就是申请了一个数组,赋值方式、数值运算与数组完全一致。 - 当然我们使用循环,对于数组的赋值并不是很方便,所以这里介绍一个新的函数
memset
:
memset的意义是将一段字节内的所有数值赋值为0。memset用法:int *p = (int*)malloc(40);memset(p, 0, 40);
要点:切记memset是对内存赋值,memset(p,1,40);
的结果是每一个4字节的数据,例如p[0],p[5]
的数据全都是0000 0001 0000 0001 0000 0001 0000 0001
即为16843009(10),所以这个函数经常用在将整个空间全部赋值为0。memset的头文件是<string.h>
或者<memory.h>
(一个int类型是4个字节,一个字节是8位二进制数) - 对于空间内数据的操作还是使用*p,这个是没有什么特殊的。
malloc注意点###
- 不要申请的空间不够用,比如要申请4字节给int类型用,千万别申请三个,这样直接导致了越界,并且导致无法释放
- 注意边界,这个有点像数组,例如
int *p = (int*)malloc(4);*(p+1) = 12
这样显然是越界操作,在编译时不会报错,但是运行时会崩溃 - 一个指针指向了一块堆区空间,千万不要再指向另一块,因为相当于重新赋值,会导致内存丢失、造成内存泄露,且形成内存碎片,如果是在循环中会无限占用内存,导致死机