什么是动态内存
动态内存是指在程序运行过程中可以被分配和释放的内存空间。这与静态内存分配相对,静态内存分配是在程序编译时就已经确定的内存空间,其大小在程序运行期间固定不变。
在许多编程语言中,特别是在C语言中,动态内存管理是通过一系列标准库函数来实现的,如`malloc()`、`calloc()`、`realloc()`和`free()`。这些函数提供了在程序运行时请求和释放内存的能力。
动态内存的分配通常发生在堆(Heap)区域,这是程序内存空间的一个部分,用于存储动态分配的内存。与之相对的还有栈(Stack),栈是用于存储函数调用信息和局部变量的内存区域,其分配和释放是自动的,通常在函数调用开始和结束时进行。
动态内存管理的意义在于它提供了灵活性,允许程序在运行时根据需要分配和释放内存,这对于实现复杂的数据结构和算法、优化资源使用、以及处理不确定大小的数据集等方面至关重要。然而,同时它也要求程序员必须谨慎使用,避免内存泄漏和野指针等问题。
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
malloc(申请函数)free(释放函数)
malloc和free的语法格式
`malloc` 函数是 C 语言标准库中的一个重要函数,用于动态分配内存。其语法如下:
void *malloc(size_t size);
这里的 `void *` 表示返回的是一个 `void` 类型的指针,实际上这个指针指向的是一个 `char` 类型的内存块。`size_t` 是一个无符号整数类型,用于指定分配的内存大小。
当你调用 `malloc` 函数时,你需要提供一个参数 `size`,表示你需要分配的内存的大小(以字节为单位)。函数返回一个指针,指向为你分配的内存区域的开始位置。如果分配成功,这个指针不会是 `NULL`;如果分配失败(比如,系统没有足够的内存来满足请求),`malloc` 函数会返回 `NULL`。
在使用 `malloc` 分配的内存时,你需要使用 `memcpy`,`strcpy` 等函数来复制数据到这个内存区域,而不是 `strcpy`,`printf` 等函数,因为这些函数不适用于动态分配的内存。
在使用完动态分配的内存后,你应该使用 `free` 函数来释放内存,以避免内存泄漏。
free(void *ptr);
这里的 `ptr` 是之前 `malloc` 或其他动态内存分配函数(如 `calloc`,`realloc`)返回的指针。使用 `free` 后,指针所指向的内存将被释放,可以被系统重新分配给其他请求。但是,免费的内存不会立即变得可被其他程序使用,这是一个操作系统和硬件相关的过程。
这里是需要把这两个函数放到一起讲解的,因为申请内存空间你就得释放内存空间,不然会导致内存泄漏,内存泄漏会导致电脑内存里面可以使用的空间越来越少。最后导致崩盘。
———————————————————————————————————————————
malloc和free函数的使用
申请内存空间
返回函数 一定要做检查
头文件stdlib.h
perror打印出错误
动态内存的开辟空间都在堆区
释放内存
传递的参数 p就可以
解释一下
所以 此时释放的是自己的权限
但是需要知道的是 此时p也就是野指针了
所以此时我们把p栓到树上
也就是设置为空指针
———————————————————————————————————————————
这里开辟内存空间注意的三点(重点)
1
这里需要进行强制类型的转化因为从语法格式我们可以看出来,这里是的void*类型,也就是不明确的类型,为什么,因为C语言官方在设定的时候不清楚你开辟内存空间的目的是干什么用的,不知道是用于整数的存储,还是用于字符的打印,还是干什么。所以这里给出无返回类型,只需要你在使用的时候进行强制类型转化就可以。这样可以提升代码的兼容性和健壮性。
2.
对于空间的释放,释放空间之后尽量的让指针指向NULL,也就是空指针,因为当指针指向的空阿金释放之后,本质上他是不指向任何的数值了,也就是此时他是野指针了,虽然你不使用他了,但是最好指向空指针。防止内存的泄露
3.
使用空间的时候,你不能让指针跟着走,我们可以看到代码里面,
使用的是这个代码*(p+i)=I+1;。而不是*(p)=i;p++;这样的代码。
为什么,因为当指针走远之后,你进行释放空间的时候,本质上释放的是指针移动后指向的空间,你让指针一直移动,移动到开辟的空间的最后一个位置,然后再进行释放空间的时候,其实本质上释放的是开辟空间之后的空间,也就是没有释放空间。
你创建了空间,但是没有释放空间,此时会导致内存泄露。
内存泄露的问题我们也聊过,也就是内存占用少的时候还不明显,但是当有大量程序运行的时候,就会导致内存一直占用,但是得不到释放。从而导致崩盘。
4.
malloc函数里面放置的是字节大小,这里放置的是字节大小,不是bit大小,这里一定要记住。
———————————————————————————————————————————
malloc和free代码
int main()
{//malloc不初始化开辟空间//开辟20个字节的空间大小 int* p = (int*)malloc(5 * sizeof(int));if (p == NULL){perror("malloc:");//这里是打印错误信息,return 1;}for (int i = 0; i < 5; i++)//这里是打印出来开辟的空间 赋值之后进行打印 {*(p + i) = i + 1;//这里是进行赋值 赋值从1开始printf("%d ", *(p + i));//这里是打印出来}free(p);//这里进行指针的释放p = NULL;//防止野指针的问题,我们指向空指针return 0;
}
malloc第两种写法
可以是sizeof(int)=四个字节
然后5*sizeof(int)=20个字节
第二种就是上述的写法malloc(20)
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
calloc(申请空间初始化
calloc(申请空间初始化)
语法格式
在C语言中,`calloc` 和 `realloc` 是用于动态内存分配的两个函数。它们都定义在头文件 `<stdlib.h>` 中。
1. `calloc`
函数用于在内存的动态存储区分配空间,并初始化每个字节为 0。其函数原型如下:
void *calloc(size_t num, size_t size);
- `num`: 分配块的数量。
- `size`: 每个块的大小(以字节为单位)。
`calloc` 返回一个指向分配内存的指针。如果分配失败,则返回 `NULL`。
———————————————————————————————————————————
calloc函数的使用
我们直接看是不直观的,我们可以这样理解
malloc(5*sizeof(int))=calloc(5,sizeof(int))
这两个是等价的
calloc函数其实和malloc函数本质都是一样的唯一不一样就是,calloc函数在开辟空间的时候会进行初始化,当然 calloc函数初始化,那运行速度也会比malloc慢一点。malloc函数初始化, 会快一点
其他的没区别
———————————————————————————————————————————
calloc和free代码
int main()
{//malloc不初始化开辟空间//开辟20个字节的空间大小 int* p = (int*)calloc(5 , sizeof(int));if (p == NULL){perror("malloc:");//这里是打印错误信息,return 1;}for (int i = 0; i < 5; i++)//这里是打印出来开辟的空间 赋值之后进行打印 {//*(p + i) = i + 1;//这里是进行赋值 赋值从1开始printf("%d ", *(p + i));//这里是打印出来}free(p);//这里进行指针的释放p = NULL;//防止野指针的问题,我们指向空指针return 0;
}
这里我们可以看到和malloc一模一样,只是换了一下函数,其他是没有变化的。
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
realloc(动态内存二次的大小调整,也就是调整空间大小)
语法格式
1. `realloc`
函数用于动态地改变之前分配的内存块的大小。其函数原型如下:
void *realloc(void *ptr, size_t size);
- `ptr`: 指向之前分配的内存块的指针。如果 `ptr` 为 `NULL`,那么 `realloc` 行为与 `malloc` 相同。
- `size`: 新分配的大小(以字节为单位)。
`realloc` 可以调整内存块的大小,如果新的大小比原来小,那么内存块可能会被移动。如果新的大小比原来大,那么内存块会尝试扩展。如果扩展失败,那么原来的内存块会被保留,只是其大小变为新的大小。`realloc` 返回一个指向调整大小后的内存块的指针。如果调整大小失败,则返回 `NULL`。
在使用这些函数时,应当总是检查返回的指针是否为 `NULL`,以避免潜在的空指针引用错误。并且在使用 `calloc` 或 `realloc` 分配的内存后,应当在不再需要时使用 `free` 函数来释放内存,以避免内存泄露。
———————————————————————————————————————————
realloc函数的使用
这个函数的代码格式可以理解为 ,你之前malloc或者calloc申请的空间是p指针进行接收的,OK此时你想对这个空间进行空间的扩展,那此时的ptr也就是p指针变量,size也就是你需要的实际空间,这里的空间不是扩展的空间,而是实际的空间
void *realloc(void *ptr, size_t size);
也就是realloc(p,40)
之前我们p开辟的是20的空间 ,这里我们实际需要的是40的空间,这不是再开辟40空间,而是一共40 的空间。
下面解释一下为什么
———————————————————————————————————————————
realloc函数扩展空间的原理
realloc函数扩展空间几乎就是两种模式
1. 直接在原内存空间的基础上进行扩展内存
2. 把原空间的内存复制到新的内存空间,建立一个更大的内存空间,需要知道的是,只要是进行内存的复制,那么就有可能导致复制失败,从而导致数据丢失。
所以实际使用的时候,我们还是需要进行判断语句的。
如图1
如图2
———————————————————————————————————————————
realloc代码解析
首先我们创建一个空间 ,然后进行调整
因为这里使用的时候 是有可能导致开辟空间错误,导致地址的丢失,所以我们进行判断,也就是主动的找一个指针变量指向新开辟的地址,只要指向的空间不是空指针,那么也就是可以满足条件,然后进行一个二次赋值
最后,不使用的时候进行申请空间的清理,防止内存空间的泄露
———————————————————————————————————————————
realloc代码
int main()
{//malloc不初始化开辟空间//开辟20个字节的空间大小 int* p = (int*)calloc(5 , sizeof(int));if (p == NULL){perror("malloc:");//这里是打印错误信息,return 1;}for (int i = 0; i < 5; i++)//这里是打印出来开辟的空间 赋值之后进行打印 {//*(p + i) = i + 1;//这里是进行赋值 赋值从1开始printf("%d ", *(p + i));//这里是打印出来}free(p);//这里进行指针的释放p = NULL;//防止野指针的问题,我们指向空指针//首先我们已经开辟动态内存空间 但是不能满足我们的需求,那么此时我们就用realloc进行空间的扩展//依旧是采取强制类型转化,然后我们这里是延伸到40个字节的空间//因为我们知道,开辟空间的时候可能会导致内存空间的扩展失败//所以我们不能直接p=realloc(int*)realloc(p, 10 * sizeof(int)); //而是进行先搞一个新的指针变量,确定成功了,再进行赋值//也就是int* ptr = (int*)realloc(p, 10 * sizeof(int));if (ptr == NULL){perror("realloc:");//打印错误信息如果是空指针return 1;}p = ptr;for (int i = 0; i < 10; i++){*(p + i) = 0;printf("%d ", *(p + i));}free(p);p = NULL;return 0;
}
这里是15个0
因为calloc也打印5个0
不要误解
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
malloc+free+calloc+realloc函数的综合使用
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
int main()
{//malloc不初始化开辟空间//开辟20个字节的空间大小 int* p = (int*)malloc(20);if (p==NULL){perror("malloc:"); return 1;}//使用这20个字节的空间printf("malloc开辟空间的使用:");for (int i = 0; i < 5; i++){*(p + i) = i + 1;printf("%d ", *(p + i));}//释放开辟的空间free(p);//释放空间后不让p是野指针 指向空指针p = NULL;printf("\n\n\n");//calloc开辟初始化空间 //这里是开辟5个sizeof 整形大小的空间 也就是20个字节大小的空间 但是 这个空间的连续的int* s = (int*)calloc(5, sizeof(int));if (s == NULL){perror("calloc:");return 1;}printf("calloc开辟空间的使用:");for (int i = 0; i < 5; i++){printf("%d ", *(s + i));}//释放开辟的空间free(s);//释放空间后不让s是野指针 指向空指针s = NULL;printf("\n\n\n");//realloc调整空间大小//这里是开辟5个sizeof 整形大小的空间 也就是20个字节大小的空间 但是 这个空间的连续的int* a = (int*)calloc(5, sizeof(int));if (a == NULL){perror("calloc:");return 1;}printf("realloc的使用\ncalloc开辟空间的使用:");for (int i = 0; i < 5; i++){*(a + i) = i + 1;printf("%d ", *(a + i));}printf("\n");//调整空间大小,但是需要知道的是 空间调整大小的时候,容易导致丢失 所以我们首先检验一下 再进行赋值int* ptr = (int*)realloc(a, 40);if (ptr == NULL){perror("realloc:"); return 1;}a = ptr;//这里虽然是对于开辟空间的使用 但是实际上也是对于空间的覆盖for (int i = 0; i < 10; i++){*(a + i) = i + 5;printf("%d ", *(a + i));}free(a);a = NULL;return 0;
}