为什么要进行动态内存分配?
一般的内存开辟方式只能开辟固定大小的内存空间,动态内存分配方式可以动态开辟内存,更加灵活。
动态内存分配函数
malloc()
函数原型
void *malloc(size_t size); //(size_t指内存块大小,以字节为单位)
函数功能
分配所需的内存空间,并返回一个指向它的指针
注意
开辟成功后,malloc会返回一个void*指针,不能直接解引用,它可以被强转为所需的数据指针类型
开辟失败后,malloc会返回一个NULL指针,所以malloc以后一定要进行检查
示例
int* p = (int*)malloc(20); //开辟一个20字节的内存空间,并且强转为int型
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
开辟成功,我们发现开辟的空间里面放的使一些随机值
对分配的内存初始化后,里面的随机值发生变化
for (int i = 0; i < 5; i++)
{
*(p + i) = i;
}
calloc()
函数原型
void *calloc(size_t nitems, size_t size);
//nitems-- 要被分配的元素个数。size -- 元素的大小。
函数功能
分配所需的内存空间,并返回一个指向它的指针。malloc 和 calloc 之间的不同点是,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。
注意
开辟成功后,calloc会返回一个void*指针,不能直接解引用,它可以被强转为所需的数据指针类型
开辟失败后,calloc会返回一个NULL指针,所以malloc以后一定要进行检查
示例
int* p = (int*)calloc(10,sizeof(int));
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
开辟成功,我们发现里面的值已经被初始化为0
我们进行初始化
for (int i = 0; i < 10; i++)
{
*(p + i) = i;
}
realloc()
函数原型
void *realloc(void *ptr, size_t size)
//ptr -- 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。
//size -- 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。
函数功能
函数尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。
注意
开辟成功后,calloc会返回一个void*指针,不能直接解引用,它可以被强转为所需的数据指针类型
开辟失败后,calloc会返回一个NULL指针,所以malloc以后一定要进行检查
示例
void test()
{
char* str;
/* 最初的内存分配 */
str = (char*)malloc(15);
strcpy(str, "小谢");
printf("String = %s, Address = %p\n", str, str);
/* 重新分配内存 */
str = (char*)realloc(str, 25);
strcat(str, "%同学");
printf("String = %s, Address = %p\n", str, str);
free(str);
str=NULL;
}
注意:
原空间后面还有多余的空间的话,在原空间后面开辟新的空间
原空间后没有对于的空间的话,重新找一片空间开辟。
free()
函数原型
void free(void *ptr)
//ptr -- 指针指向一个要释放内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果传递的参数是一个空指针,则不会执行任何动作。
函数功能
释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。
常见的动态内存错误
- 对NULL指针进行操作
//1. 对NULL指针进行操作
int* p1 = (int*)malloc(10 * sizeof(int));
//没有对开辟的空间进行检查,如果申请空间失败就是对空指针操作
//assert(p1); //应进行检查
for (int i = 0; i < 10; i++)
{
*(p1 + i) = i;
}
free(p1);
p1=NULL;
- 对动态开辟空间的越界访问
//2. 对动态开辟空间的越界访问
int* p2 = (int*)malloc(10 * sizeof(int));
assert(p2);
for (int i = 0; i <= 10; i++) //初始化11个整数
{
*(p2 + i) = i;
}
free(p2)
p2=NULL;
- free释放非动态开辟的内存
//5. free释放非动态开辟的内存
int a[10] = { 0 };
free(a);
- 使用free释放了动态开辟空间的一部分
//4. 使用free释放了动态开辟空间的一部分
int* p4 = (int*)calloc(10, sizeof(int));
//int* temp=p4;
for (int i = 0; i < 5; i++)
{
*p4++ = i;
}
free(p4);
//free(temp);
temp=NULL;
- 忘记释放动态开辟的空间
//3. 忘记释放动态开辟的空间
int* p3 = (int*)malloc(10 * sizeof(int));
assert(p3);
for (int i = 0; i <= 10; i++)
{
*(p3 + i) = i;
}
//free(p2); //忘记释放
//p2=NULL;
- 对同一块空间进行多次释放
//6. 对同一块空间进行多次释放
int* p6 = (int*)malloc(20);
free(p6);
//解决方案,在free(p6)后对p6进行复制为NULL,这样即使再次free(p6)也是对空指针操作
//p6=NULL;
free(p6);