掌控内存
好菜终于端上来了,学到靠自己分配内存了。学完这一节,对内存有了崭新的认知,也开始接触到堆栈了。以前的学习中,都是让系统和编译器来分配内存,我们只是告诉他们要什么样的内存而已。而真要到我们自己分配内存的时候,就需要用到头文件stdlib.h
了,它包含了malloc()
函数和free()
函数.
malloc()函数--分配内存
malloc()
是分配内存的函数,返回值是分配的内存块的首内存地址(如果分配内存失败,返回值为NULL)。如果我们要靠这个函数来声明一个长度为10个元素的整数数组,就需要:
int *ptr;
ptr = (int *)malloc(10*sizeof(int));
(int *)malloc(10*sizeof(int))
的含义是,分配一个可以容纳10个整数的内存块,10*sizeof(int)
本质上还是个无符号整数,换算成字节就是40个字节(10 * 整数的字节4)。这个函数分配完内存空间后,返回的地址,需要用指针存储起来,那么就先声明一个指向整数的指针,然后储存内存块的地址。这里有意思的地方是,malloc()
前面加入了强制转换类型的语句(int *)
,这意味着ptr及时不是指向int的指针,在这条语句之后也会被强制转换成指向int的指针,这既保证了代码可读性和安全性,也是C++的标准(C++必须要强制转换,C则不必要)。
在变长数组出现前,如果想要分配由变量来定义长度的数组,就得用到malloc()
,如:
int n = 10;
int *ptr;
ptr = (int *)malloc(n*sizeof(int));
实际上,这样比变长数组更加灵活,但变长数组也有优点,就是变长数组属于自动数据类型,而用malloc()
分配的内存属于动态数据类型,需要下文讲到的free()
搭配使用来妥善管理内存。
为什么一定要使用sizeof来配合malloc()
使用呢,一是为了代码可读性,而是增强代码可移植性,有些平台的数据类型不一定是那个内存长度。
free()函数--释放内存
free()
是释放内存的函数,必须与malloc()
或者calloc()
搭配使用。由malloc()
分配出来的内存,在程序不再使用它后,一定要用free()
来释放掉这些内存,如果不这样,那么就是经典的“从入门到内存泄漏”,内存泄漏就是malloc()
不断创造内存空间,直到内存满了,无法容纳,程序就出错,内存溢出来了。
作为C程序员,不要指望操作系统自动来清理内存,要靠自己精准掌控内存。
重新审视数据类型
学到这里,从内存的角度来看,数据类型分为三种:
- 静态数据类型:编译后在正式运行前就分配了内存,并且直到程序结束才被清除掉内存的数据。
- 自动数据类型:只有程序进入属于它的作用域时,才被分配内存,在程序推出它的作用域后就会被清除掉内存的数据。变长数组是自动数据类型,这也是它与
malloc()
分配生成的数组不同的地方,也是优点。自动数据类型通常以栈的形式来处理,这意味着创建的变量按顺序加入内存,按相反顺序被清理出内存。 - 动态数据类型:由
malloc()
函数生成的内存,可以由程序员自定义内存空间大小,被分配后就一直存在,直到用free()
清理掉。
PS:由于动态数据类型在内存中很容易形成支离破碎分散的位置,使得动态内存通常都比栈内存速度慢很多。