C语言的一个特性是接近底层,对于硬件的控制能力比其他高级动态语言要强。同时,C语言赋予程序员更大的自由度,更信任程序员。在内存的分配与释放上,我们知道非静态变量(块作用域,无链接,自动生存期)在程序进入到变量定义所在的地方(块或函数内)时分配内存,在离开块作用域时释放。对于静态变量,在程序加载到内存时,变量的内存分配到静态存储区,且直到程序运行结束(终止)时释放。这些都是程序执行过程中由程序自动控制的。
同时,C提供给程序员更大的权限,可以手动控制变量的内存分配和释放,相关的函数为malloc(),calloc()和free()。
手动分配内存malloc()和calloc()
malloc():函数原型:void *malloc(size_t size),参数为要分配的内存字节数,返回值为指向void的指针,pointer-to-void,一般返回该段内存空间的首个字节。我们可以使用类型转化将分配的内存起始地址赋值给一个指针变量,这样我们就可以操作这段内存空间内的数据,并在必要时候释放该段内存空间。
double * ptd;
ptd = (double *) malloc(30 * sizeof(double));
我们首先声明一个指向double类型的指针变量ptd,然后使用malloc申请包含30个double类型的一段内存空间,并将其内存空间的起始地址赋值给ptd。我们甚至可以用变量n替换30这个常量,这样相当于我们可以动态创建一个可变数组,类似VLA。若malloc分配内存失败,则返回空指针NULL。
calloc()和malloc类似,函数原型为:void *calloc(size_t nitems, size_t size)。它有两个参数,第一个为要申请的内存单元数,第二个为每个内存单元的字节数。malloc和calloc的区别在于calloc会将分配的这段内存里的每个元素设置为0(bit位为 0),相当于一个初始化的作用。
使用free() 释放内存
一般使用malloc分配可一段内存空间后,需要在必要的时候手动释放掉,以防止内存泄露。free()和malloc()配合使用。
示例:
/* dyn_arr.c -- dynamically allocated array */
#include<stdio.h>
#include<stdlib.h> /* for malloc(), free() */
int main(void)
{
double * ptd;
int max = 0;
int number = 0;
int i = 0;
puts("What is the maximum number of type double entries?");
if (scanf("%d", &max) != 1)
{
puts("Number not correctly entered -- bye.");
exit(EXIT_FAILURE);
}
ptd = (double *) malloc(max * sizeof(double));
if (ptd == NULL)
{
puts("Memory allocation failed. Goodbye.");
exit(EXIT_FAILURE);
}
/* ptd now points to an array of max elements */
puts("Enter the values (q to quit):");
while (i < max && scanf("%lf", &ptd[i]) == 1)
++i;
printf("Here are your %d entries:\n", number = i);
for (i = 0; i < number; i++)
{
printf("%7.2f ", ptd[i]);
if (i % 7 == 6)
putchar('\n');
}
if (i % 7 != 0)
putchar('\n');
puts("Done.");
free(ptd);
return 0;
}
输出:
What is the maximum number of type double entries?
5
Enter the values (q to quit):
20 30 35 25 40 80
Here are your 5 entries:
20.00 30.00 35.00 25.00 40.00
Done.
虽然我们输入了6个integer类型的数据,但实际输出只有5个,因为我们申请的这段内存空间为包含5个integer类型的数组。
free()的重要性
若使用malloc或calloc手动分配内存,且没有使用free释放掉该段内存,某些操作系统会自动释放掉这段内存,但不是所有的操作系统都支持这种特性。所以,不要依靠操作系统,而是要手动释放掉这段内存。
原因:来看下面这段代码:
int main()
{
double glad[2000];
int i;
for (i = 0; i < 1000; i++)
gobble(glad, 2000);
}
void gobble(double ar[], int n)
{
double * temp = (double *) malloc(n * sizeof(double));
/* free(temp); // forgot to use free() */
}
main函数每次调用gobble时,都会使用malloc手动申请2000个doube类型的内存空间,假设每个double类型占用8个字节(bytes)。那么执行到malloc处时,总共会申请16000字节的空间,而我们没有调用free函数手动释放掉这段内存空间,当该函数执行完毕时,指向这16000个字节空间的指针变量temp(块作用域,无链接,自动生存期)将会消失,但这16000个字节的内存空间仍然存在,且程序无法通过任何变量或方式访问该内存空间。
第二次调用gobble时,仍然会出现这种情况,直到for循环结束时,我们总共手动申请了1000 * 2000 * 8 = 16000000字节的内存空间,很有可能没到这么多内存时,内存已经消耗完毕。这种情况,我们称为内存泄漏。所以,我们要时刻记得使用free释放掉malloc手动分配的内存空间。