C语言学习笔记
堆区空间申请(二)
本文将以堆区空间申请(一)为基础,继续讨论堆区空间的申请与释放及相关函数的应用。
free释放空间
- 函数的原型:
void free(void *memblock);
- 函数的写法:
free(p);
free经常是在我们不再需要用到该堆区空间的时候将其进行释放。void
表示无返回值,主函数void
表示无参数,也就是其他什么都不用管,直接free§就可以了,这里的p表示申请空间的首地址。 - **注意点:**1、free后的指针,空间就不能再用了,所以一般指针释放后,赋值一个
NULL
。但是即使把malloc过的p的空间释放了,p的地址也不会变。没有具体指向的指针,或者说所指向空间访问受限的指针,叫野指针,这个东西不能直接使用;
2、不能重复释放某一个空间,这将导致程序崩溃
3、不能释放栈区空间
4、一定要释放头指针。 - 崩溃常见的有两种,程序运行时的崩溃,一定是某一行代码引起的;运行结束的崩溃,基本上就是内存越界操作了,非常难找,所以一定要细心地使用指针。
- 使用malloc申请的空间,在系统结束的时候会自动释放,但是还是要使用free,因为在实际使用情况下,服务器不会进行重启,否则会严重影响用户体验,这是软件公司和程序员的大忌。
malloc一维数组
- 定义方式和栈区数组不同,但是使用完全一样。其实
int *p = (int*)malloc(sizeof(int)*5);
这样的语句就是在定义一个堆区数组了 - 需要对数组进行赋值,可以用循环赋值成指定的值,也可以用memset赋值成0.
- malloc数组可指定任意长度,在任何需要定义时都可以进行操作。因此他也叫动态定义数组,或者动态空间
- 变(量)长数组写法:
const int n = 12;
int a[n];
int n;
scanf("%d",&n);
int a[n];
这两种都是变长数组的正确写法,但是有一些编译器可能没有引入,其意义是一个能自定义数组长度的变量,这和malloc在堆区空间中申请数组异曲同工
malloc与一维二维数组指针
- 写法:
int (*p)[5] = (int(*)[5])malloc(sizeof(int)*5);
注意小括号一定不能丢了,否则就是指针数组了 - 若要对上述堆区数组中第3项赋值,应使用下列语句
*(*p+2) = 3;
这里的*p意义和栈区数组中a相同,即可以理解为a[2],公式***(*p+n) = (*p)[n]
** - 对于二维数组指针:
int (*p)[2][3] = (int(*)[2][3])malloc(sizeof(int)*2*3);
,这样的语句相当于在栈区中申请如下的数组:int a[2][3];
int(*p1)[2][3] = &a;
对其中的数据赋值尽量用方括号的形式,更加容易理解。这一部分的难点还是和指针有关的操作详见指针学习笔记
calloc函数介绍
- 功能:申请一段数组空间并且自动全部初始化为0
- 函数原型:
void *calloc(size_t num,size_t size);
- 头文件:
<stdlib.h>
和<malloc.h>
- 用法:
int *p = (int*)calloc(3,4)
其中3是数组中的数据个数,4是每个数据所占字节数大小
realloc函数介绍
- 功能:重新分配内存大小
- 函数原型:
void *realloc(void *memblock,size_t size);
- 头文件:
<stdlib.h>
和<malloc.h>
- 用法:
int *p1 = (int*)realloc(p,20);
第一个参数是被操作空间的首地址,第二个参数是要分配的大小 - 这里介绍另一个函数:
_msize(p)
,其返回值是p的字节数大小,可以用作测试realloc的功能 - 由于realloc一定会申请一段连续的空间,所以如果原来的内存空间不够,则将其转化为内存碎片,并前往其他内存空间申请,也就是说其首地址可能会发生变化;如果申请的空间过大,则返回值
NULL
。
calloc和malloc的选择
- 使用场景:calloc申请数组比较好用,其他数据结构,比如链表,树,图,一次申请sizeof(节点),这些用malloc更合适,因为calloc是自动清零的。
- 效率问题:calloc会初始化内存,所以申请的内存数很多的时候,效率会低一点点。毕竟有时候内存不需要初始化,malloc的效率高一些。不过现代电脑的这点效率问题就是微乎其微了。
*的四种作用
- 声明的时候有*,表示指针变量
int *p
- *+地址,表示地址操作符
*p
- 数字*数字,表示乘法
1*2
- 注释
/* */