在C中动态分配内存的基本步骤有:
- 用
malloc
类的函数分配内存; - 用这些内存支持应用程序;
- 用
free
函数释放内存。
这个方法在具体操作上可能存在一些小变化,不过这里列出的是最常见的。在下例中,我们用malloc
函数为整数分配内存。指针将分配的内存赋值为5,然后内存被free
函数释放。
int *pi = (int*) malloc(sizeof(int));
*pi = 5;
printf("*pi: %d\n", *pi);
free(pi);
当这段代码执行时会打印数字5。图2-1说明了在free
函数执行之前内存如何分配。为方便在本章说明问题,除非特别指出,我们假定示例代码出现在main
函数中。
图2-1:整数的内存分配
malloc
函数的参数指定要分配的字节数。如果成功,它会返回从堆上分配的内存的指针。如果失败则会返回空指针。测试所分配内存的指针是否有效在C malloc函数中讨论。sizeof
操作符使应用程序更容易移植,还能确定在宿主系统中应该分配的正确的字节数。
在本例中,我们试图为整数分配足够多的内存。假定长度是4,我们可以这么写:
int *pi = (int*) malloc(4);
然而,依赖于系统所用的内存模型,整数的长度可能会发生变化。可移植的方法是使用sizeof
操作符,这样不管程序在哪里运行都会返回正确的长度。
注意 涉及解引操作的常见错误见下面的代码:
int *pi; *pi = (int*) malloc(sizeof(int));
问题出在赋值符号的左边。我们在解引指针,这样会把
malloc
函数返回的地址赋给pi
中存放的地址所在的内存单元。如果这是第一次对指针进行赋值操作,那指针所包含的地址可能无效。正确的方法如下所示:pi = (int*) malloc(sizeof(int));
这种情况下不应该用解引操作符。
稍后也会深入讨论free
函数,它和malloc
协同工作,不再需要内存时将其释放。
注意 每次调用
malloc
(或类似函数),程序结束时必须有对应的free
函数调用,以防止内存泄漏。
一旦内存被释放,就不应该再访问它了。通常我们不会在释放内存后有意去访问,不过,就像迷途指针中所说的,这也有可能意外发生。在这种情况下系统的行为将依赖于实现。通常的做法是总是把被释放的指针赋值为NULL
,free函数会讨论这一点。
分配内存时,堆管理器维护的数据结构中会保存额外的信息。这些信息包括块大小和其他一些东西,通常放在紧挨着分配块的位置。如果应用程序的写入操作超出了这块内存,数据结构可能会被破坏。这可能会造成程序奇怪的行为或者堆损坏,指针的使用问题会演示相关示例。
考虑如下代码段,我们为字符串分配内存,让它可以存放最多5个字符外加结尾的NUL
字符。for
循环在每个位置写入0,但是没有在写入6字节后停止。for
语句的结束条件是写入8字节。写入的0是二进制0而不是ASCII字符0的值。
char *pc = (char*) malloc(6);
for(int i=0; i<8; i++) {
pc[i] = 0;
}
在图2-2中,6字节的字符串后面还分配了额外的内存,这是堆管理器用来记录内存分配的。如果我们越过字符串的结尾边界写入,额外的内存中的数据会损坏。在本例中,额外的内存跟在字符串后面。不过,实际的位置和原始信息取决于编译器。
图2-2:堆管理器用到的额外内存
评论前必须登录!
注册