在使用 C 语言编程的过程中,不可能所有的变量都直接分配在栈上。并且栈上的空间是十分有限的。这个时候就需要手动的在堆上分配和管理内存。
主要的几个函数为:
void *malloc(size_t size);
void *calloc(size_t num, size_t size);
void *realloc(void* ptr, size_t new_size);
void free(void *ptr);
别忘记引入头文件stdlib.h
内存分配
malloc
1 2
| void* malloc(size_t size);
|
- 此函数会在堆空间上分配一片 连续的 ,size 个字节大小 的内存块。此函数 不会对内存块中的数据进行初始化,内存块中的数据是随机未定义的。
- 如果 分配成功 ,此函数会返回指向该内存块地址(首字节地址) 的指针。注意返回的指针类型是void 指针,在操作之前需要进行转换。
- 如果 分配失败 ,此函数会返回一个 空指针(NULL)。
calloc
1 2 3 4 5 6
|
void* calloc(size_t num, size_t size);
|
- 此函数也会在堆空间上分配一片连续的内存空间,但不同的是,它基于元素的个数以及每个元素的大小来进行内存分配,所以calloc 常用于在堆上分配数组的内存空间。
- 初始化为零 :calloc 最重要的特性之一是它会自动将分配的内存初始化为零。 这意味着不仅仅是分配内存,它还清零所有内存。
- 返回值在分配成功和失败时,和 malloc 是一致的。
使用 malloc 与 calloc 的建议
- malloc 由于不需要初始化 0 值,性能可能会更好一些。所以在特别在意性能以及内存确实手动初始化时,优先选择用 malloc 函数。
- calloc 的优点是安全。如果使用 malloc 函数分配内存,那么内存块中的所有元素都只有一个随机值,此时若忘记初始化直接使用这些随机值就会产生未定义行为,这是非常危险的。而这个危险,可以通过使用 calloc 函数解决。
- 在实际应用中,特别是当程序安全和正确性是首要考虑时,在两个函数都可用时,那么请选择使用 calloc 函数。
realloc
1 2 3 4 5 6
|
void* realloc(void* ptr, size_t new_size);
|
- 如果 ptr 指针是一个空指针,那么该函数的行为和 malloc 一致。
- 如果 new_size 的取值为 0,那么该函数的行为和 free 一致。
- 当 new_size 的取值和已分配的内存块大小一致时,此函数不会做任何操作。
- 当 new_size 的取值比已分配的内存块小时,会在旧内存块的尾部 (高地址) 截断,被截断抛弃的内存块会被自动释放。
- * 当 new_size 的取值比已分配的内存块大时,会尽可能地尝试原地扩大旧内存块(这样效率高);如果无法原地进行扩大,则会在别处申请空间分配 new_size 大小的新内存块,并将旧内存块中的数据全部复制进去后,将旧内存块自动释放。*
- 不管采用哪种方式扩展旧内存块,新扩展部分的内存区域都不会初始化,仍只具有随机值。
- 如果 realloc 函数分配内存空间成功,它会返回指向新内存块的指针。
- 若失败,仍会返回空指针,且不会改变旧内存块。
内存释放
free
- 参数必须是堆上申请内存块的地址 ( 首字节地址 ),不能传递别的指针, 否则会引发未定义行为。
- free 函数并 不会修改它所释放的内存区域中存储的任何数据 。free 的作用 仅仅是告诉操作系统这块内存不再被使用 了,可以将其标记为可用状态,以供将来的内存分配请求使用。
- 释放后的内存区域中的数据一般仍然会继续存在,直到被下一次的内存分配操作覆盖。当然即便 free 前的原始数据一直存在未被覆盖,这片内存区域也不再可用了,因为你不知道什么时候数据就会被覆盖掉了。
- free 函数不会修改传入指针指向的内容,更不会对实参指针本身做任何修改。