C语言基础第18天:动态内存分配
动态内存分配
要实现动态内存分配,需要使用标准C库提供的库函数,我们所说的动态内存分配,其实就是在堆区申请内存(此时的内存回收需要程序员自身来维护)。
常用函数
malloc
头文件:
#include <stdlib.h>
函数原型:
void* malloc(size_t size);
功能:分配指定字节数的内存到堆区,返回指向内存块首地址的指针。内存内容未初始化(随机值)。
参数:
size :要分配的内存大小(字节),这里的size_t是数据类型 unsigned long int 的别名。
返回值:
成功 :返回内存指针(申请到的内存的首地址)。
失败 :失败返回 NULL。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{// 新建一个指针变量,用来接收内存分配后返回的堆内存首地址int *p = malloc(sizeof(int)); // 4字节 这里尽量不使用常量// 对于指针的使用,一定要进行有效性校验,防止野指针if(p == NULL)// 等价于 !p{perror("内存申请失败!");// 这个函数用于向控制台输出异常信息return -1;}// malloc申请的内存空间,默认填充的是随机值,需要我们手动清零memset(p,0,sizeof(int)); // 对指定空间赋值0,支持批量赋值// 向这块空间赋值*p = 100;// 访问这块空间printf("%d\n", *p);// 堆空间使用完毕,一定要释放内存,此时需要程序员写代码释放free(p);// 如果指针对应的内存被释放,此时指针需要置空,防止产生空悬指针p = NULL;return 0;
}
注意事项:
分配内存后需要手动初始化内存,推荐 memset 或 calloc。
内存空间连续,不可越界访问。
calloc
头文件:
#include <stdlib.h>
函数原型:
void* calloc(size_t nitems, size_t size);
功能:动态分配内存,并初始化为0。
参数:
nitems :元素个数。
size :每个元素的字节大小。
返回值:
成功 :返回内存指针(申请到的内存的首地址)。
失败 :失败返回 NULL。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define LEN 5int main()
{// 创建一个指针变量,用来接收内存分配后返回的堆内存地址int *arr = calloc(LEN, sizeof(int)); // 等价于 int *arr = malloc(LEN * sizeof(int); memset(arr,0,LEN * sizeof(int));// 对于指针的使用,一定要进行非空检验,防止野指针if (arr == NULL) // 等价于 !arr{perror("内存申请失败!");return -1;}// 使用for循环快速赋值for (int i = 0; i < LEN; i++){if(i % 2 == 0){continue;}arr[i] = (i+1)*10;}// 遍历数组int *p = arr;for ( ; p < arr + LEN ; p++){printf("%-6d", *p);}printf("\n");// 内存使用完毕,释放内存free(arr);// 置空,防止产生空悬指针arr = p = NULL;return 0;
}
realloc
头文件:
#include <stdlib.h>
函数原型:
void* realloc(void* ptr, size_t size);
功能:调整已分配内存块的大小,可能迁移数据到新地址。扩容后,空余位置是随机值,需要手动清零。
原理:
当前内存空间后面的剩余空间足够扩容,就在原空间基础上进行扩容,返回原地址。
当前内存空间后面的剩余空间不够扩容,会重新开辟一块空间,将原空间数据按顺序拷贝后,销毁原空间,返回新地址。扩容后的内存空间中的数据是随机的,需要手动清零。
参数:
ptr :原内存指针,需要扩容的内存空间的指针,这个ptr的来源( malloc/calloc/realloc )。
size :指定重新分配内存的大小(字节)。
返回值:
成功 :返回内存指针(申请到的内存的首地址)。
失败 :失败返回 NULL。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define LEN 5
#define NEW_LEN 8
#define NEW_LEN_MIN 3int main()
{// 创建一个指针,指向堆内存分配的内存空间int *arr = (int*)calloc(LEN, sizeof(int));// 指针的非空检验,防止野指针if (!arr){perror("内存申请失败!");return -1;}// 使用for循环赋值for (int i = 0; i < LEN; i++){arr[i] = (i + 1) * 10;}// 遍历数组int *p = arr;for ( ; p < arr + LEN; p++){printf("%-6d", *p);} printf("\n");// 扩容数组int *temp = (int*)realloc(arr, NEW_LEN * sizeof(int)); // 扩容出来的空间,默认是随机值// 非空校验,防止野指针if(!temp){perror("内存申请失败!");free(arr); // 扩容失败,回收原空间return -1;}// 更新数组指针arr = temp;// 对扩容部分清零memset(arr+LEN,0,(NEW_LEN - LEN) * sizeof(int));// 修改数组元素arr[7] = 666;// 遍历数组for (int i = 0; i < NEW_LEN; i++){printf("%-6d", *(arr + i));}printf("\n");// 缩容数组temp = (int*)realloc(arr, 0); // 此时,会回收掉内存,等价于free(arr);// 非空校验,防止野指针if(!temp){perror("内存申请失败!");return -1;}// 更新数组指针arr = temp;// 遍历数组for (int i = 0; i < NEW_LEN; i++){printf("%-6d", *(arr + i));}printf("\n");// 释放内存free(arr);arr = NULL;p = NULL;return 0;
}
free
头文件:
#include <stdlib.h>
函数原型:
void free(void* ptr);
功能:释放动态分配的内存。
注意事项:
只能释放一次,重复释放会导致程序崩溃。
释放后应将指针置为 NULL ,避免野指针。
栈内存由系统自动释放,无需手动 free 。