导言:
我们知道数组的创建也会在内存中开辟空间,但是开辟空间的方式有两个特点:
1. 空间开辟大小是固定的。
2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道那数组的编译时开辟空间的方式就不能满足了。这时候就只能进行动态内存申请。在C语言中,动态内存管理是非常重要的,它允许程序在运行时动态地分配和释放内存。在这篇博客中主要将介绍malloc、calloc、realloc和free这四个与动态内存管理密切相关的函数,以及它们之间的区别和用法。
目录
正文:
一.malloc函数
malloc函数是C语言中用于动态分配内存的函数之一。这个函数可以向内存申请一块连续可用的空间,并返回指向这块空间的指针。函数原型如下:
void *malloc(size_t size);
size:表示要分配的内存大小,类型为size_t。size_t是一种无符号整数类型,用于表示内存的大小。通常可以使用sizeof运算符来获取需要分配的内存大小。
在使用malloc函数时,需要注意以下几点:
- 分配的内存块大小应该合适,不要过大或过小。过大的内存块可能导致内存浪费,过小的内存块可能无法满足程序的需求。
- 分配的内存块应该及时释放,避免出现内存泄漏的情况。可以使用free函数释放已经分配的内存块。
- 在使用分配的内存块之前,应该进行合适的初始化操作,确保内存块的内容符合程序的要求。
- 分配的内存块可能会被其他函数或指针访问,因此在使用之前需要确保内存块的有效性。
- 如果开辟成功,则返回一个指向开辟好空间的指针。
如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
返回值的类型是void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
如果参数size 为0,malloc的行为是标准是未定义的,取决于编译器。
使用案例:
#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>
int main() {
int* ptr;
int size;
printf("请输入数组的大小:");
scanf("%d", &size);
// 使用malloc函数动态分配内存空间
//malloc是一个void*类型,可以强制类型转化成我们所需要的类型
ptr = (int*)malloc(size * sizeof(int));
if (ptr == NULL) {
//可能开辟失败了,如果失败打印下错误信息
perror("malloc");
}
printf("请输入%d个整数:", size);
for (int i = 0; i < size; i++) {
scanf("%d", &ptr[i]);
}
for (int i = 0; i < size; i++) {
printf("%d ", ptr[i]);
}
free(ptr);
return 0;
}
运行结果如下:
malloc函数是C语言中用于动态分配内存的函数,它可以根据程序的需要分配合适大小的内存块。使用malloc函数需要注意合理分配内存大小、及时释放内存、进行内存的初始化操作等。通过合理使用malloc函数,可以更好地管理内存资源,提高程序的性能和可靠性。
二.calloc函数
calloc函数和malloc函数非常相似,也是C语言中用于动态分配内存的函数之一。
函数原型为:
void *calloc(size_t num, size_t size);
calloc可以接受两个参数,分别表示要分配的元素数量和每个元素的大小,返回一个指向分配内存块的指针。如果分配成功,则返回的指针指向一块连续的、已初始化为0的内存;如果分配失败,则返回NULL。
与malloc函数不同的是,calloc函数在分配内存时需要指定元素的数量和每个元素的大小。这样,calloc函数会根据这两个参数计算出需要分配的总内存大小,并分配相应大小的内存块。calloc函数在分配内存时,还会自动将分配的内存块初始化为0。这意味着在使用calloc分配的内存块之前,不需要手动进行初始化操作。
在使用calloc函数时,需要注意以下几点:
- 分配的内存块大小应该合适,不要过大或过小。过大的内存块可能导致内存浪费,过小的内存块可能无法满足程序的需求。
- 分配的内存块应该及时释放,避免出现内存泄漏的情况。可以使用free函数释放已经分配的内存块。
- 分配的内存块可能会被其他函数或指针访问,因此在使用之前需要确保内存块的有效性。
使用案例:
#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("请输入数组的大小: ");
scanf("%d", &n);
// 使用calloc函数分配n个int类型元素的内存块
int* arr = (int*)calloc(n, sizeof(int));
if (arr == NULL) {
//开辟失败打印错误信息
perror("calloc");
}
// 对分配的内存块进行初始化
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
// 打印数组元素
printf("数组元素:\n");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// 释放内存块
free(arr);
return 0;
}
运行结果:
为了与malloc区分这里在观察一下两个函数的内存图:
calloc:
可以看见calloc将元素全都初始化为了0。
malloc:
可以看见malloc并没有对元素进行初始化,至于里面的cd cd cd cd只是因为在vs编译器下的效果,其值为 -842150451,不同编译器下会有不同的结果。
calloc函数是C语言中用于动态分配内存的函数,它可以根据程序的需要分配合适大小的内存块,并自动将内存块初始化为0。使用calloc函数需要注意合理分配内存大小、及时释放内存等。通过合理使用calloc函数,可以更好地管理内存资源,提高程序的性能和可靠性。
三.relloc函数
realloc函数的出现让动态内存管理更加灵活。有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时候内存,我们一定会对内存的大小做灵活的调整。那realloc 函数就可以做到对动态开辟内存大小的调整。
函数原型如下:
void *realloc(void *ptr, size_t size);
realloc函数接受两个参数:
ptr
:一个指向之前分配的内存块的指针。如果该指针为NULL,则realloc函数的行为类似于malloc函数,即分配一个新的内存块。size
:重新分配后的内存块的大小,以字节为单位。- realloc函数的返回值是一个指向重新分配后的内存块的指针。如果内存重新分配成功,则返回的指针与
ptr
相同;如果内存重新分配失败,则返回NULL。
realloc函数的功能是在已经分配的内存块上调整大小。它可以用于扩大或缩小内存块的大小。如果重新分配后的大小大于原来的大小,realloc函数会尝试扩大内存块的大小;如果重新分配后的大小小于原来的大小,realloc函数会尝试缩小内存块的大小。在扩大或缩小内存块的过程中,realloc函数会尽可能地保留原来内存块中的数据。
在使用realloc函数重新分配内存块的过程中,有两种情况需要考虑:
内存块扩大:当重新分配后的大小大于原来的大小时,realloc函数会尝试扩大内存块的大小。在这种情况下,可能会发生以下两种情况:
- 如果原来的内存块后面有足够的连续空闲内存可供扩展,realloc函数会直接在原来的内存块后面扩展内存块的大小,并返回指向扩展后内存块的指针。
- 如果原来的内存块后面没有足够的连续空闲内存可供扩展,realloc函数会在内存堆中寻找一个足够大的连续空闲内存块,将原来的内存块的内容复制到新的位置,并释放原来的内存块。然后,返回指向新位置的内存块的指针。
2.内存块缩小:当重新分配后的大小小于原来的大小时,realloc函数会尝试缩小内存块的 大小。在这种情况下,可能会发生以下两种情况:
- 如果缩小后的大小与原来的大小相差不大,realloc函数可能会直接在原来的内存块中调整大小,并返回指向调整后内存块的指针。
- 如果缩小后的大小与原来的大小相差较大,realloc函数会在内存堆中寻找一个足够大的连续空闲内存块,将原来的内存块的内容复制到新的位置,并释放原来的内存块。然后,返回指向新位置的内存块的指针。
使用案例:
#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>
int main() {
int* ptr;
int size;
printf("请输入要开辟的大小:");
scanf("%d", &size);
// 分配初始大小的内存块
ptr = (int*)malloc(size * sizeof(int));
if (ptr == NULL) {
//开辟失败打印信息
perror("malloc");
}
// 输入数组元素
printf("请输入%d个值:\n", size);
for (int i = 0; i < size; i++) {
scanf("%d", &ptr[i]);
}
// 扩大内存块的大小
int newSize;
printf("请输入新空间的大小: ");
scanf("%d", &newSize);
//扩大\s缩小开辟的大小
ptr = (int*)realloc(ptr, newSize * sizeof(int));
if (ptr == NULL) {
perror("calloc");
}
// 输入新增加的元素
printf("输入增加的元素%d\n", newSize - size);
for (int i = size; i < newSize; i++) {
scanf("%d", &ptr[i]);
}
// 输出数组元素
printf("数组元素:\n");
for (int i = 0; i < newSize; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// 释放内存
free(ptr);
return 0;
}
运行结果:
realloc函数也是一个有用的内存管理函数,可以在运行时动态地调整内存块的大小。使用realloc函数可以避免频繁地分配和释放内存块,提高内存的利用率和程序的性能。
四.free函数
动态内存分配是一项重要的功能,可以通过malloc、calloc和realloc函数来动态地分配内存。然而,为了避免内存泄漏和优化内存使用,我们还需要学会释放已经分配的内存。
函数原型:
void free(void *ptr);
参数ptr是一个指向之前分配的内存块的指针。当调用free函数时,该内存块将被释放,并可以被再次分配给其他变量使用。
1.free函数的基本用法
- free函数是用来释放动态开辟的内存。
- 如果参数ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
- 如果参数ptr 是NULL指针,则函数什么事都不做
- 参数ptr是一个指向之前分配的内存块的指针。当调用free函数时,该内存块将被释放,并可以被再次分配给其他变量使用。
2.free函数的注意事项
- 只能释放动态分配的内存:free函数只能用于释放通过malloc、calloc或realloc函数分配的动态内存。如果试图使用free函数释放静态或自动分配的内存,将会导致未定义的行为。
- 释放空指针是安全的:如果传递给free函数的指针是NULL,即空指针,free函数将不会执行任何操作。因此,可以安全地将空指针传递给free函数,而无需担心出错。
- 不要释放已经释放的内存:在调用free函数之后,应该避免再次使用已经释放的内存块,否则可能会导致未定义的行为。为了避免这种情况,建议在调用free
上面的案例中都在末尾使用了free函数进行释放,对于动态开辟的空间,必须由程序员自己释放,或者是程序结束后系统会释放。所以在开辟一段空间后,在使用完后一定要记得释放,不然有可能会造成内存泄漏或者野指针的问题导致程序崩溃。
总结:
动态内存管理是C语言中非常重要的概念,malloc、calloc、realloc和free这四个函数为我们提供了灵活地分配和释放内存的能力。了解它们的用法和区别,能够帮助我们更好地管理内存资源,避免内存泄漏和内存溢出等问题。在使用这些函数时,我们应该注意检查返回的指针是否为NULL,避免重复释放已经释放的内存块,并避免使用已经释放的内存块的指针。通过合理使用这些函数,我们可以更好地控制内存的分配和释放,提高程序的性能和可靠性。