write in front
大家好,我是謓泽,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流
2021年度博客之星物联网与嵌入式开发TOP5~2021博客之星Top100~阿里云专家博主 & 星级博主~掘金⇿InfoQ创作者~周榜71﹣总榜1479
本文由 謓泽 原创 CSDN首发如需转载还请通知⚠
个人主页⇲打打酱油desuCSDN博客
欢迎各位→点赞 + 收藏⭐️ + 留言
系列专栏⇥【C】系列_謓泽的博客-CSDN博客[₀~¹]
✉️我们并非登上我们所选择的舞台,演出并非我们所选择的剧本
葵花宝典
write in front
静态内存开辟₀
动态内存开辟¹
栈
堆
malloc()函数
free()函数
malloc()动态内存开辟使用
calloc()
calloc()动态内存开辟使用
realloc()
realloc()动态内存开辟使用
内存开辟常见错误
再说动态内存开辟的时候,我们先来介绍下我们已经学会的开辟内存空间的方法吧。
char arr[10] = {0}; //在所处的栈上连续开辟10个字节的内存空间
int a = 1; //在栈上开辟4个字节空间
这些就是我们前面所学的知识点常用开辟内存空间的办法↓
- 我们所开辟的内存空间大小是固定的。
- 我们在申明数组的时候,必须要指定数组当中的长度,这样它所需要的内存空间才能被编译系统所知道。从而得到内存上的一个分配。
但是这样就会导致一个问题,当我们有的时候需要的空间大小必须是要在程序运行之后才能够被知道,那用数组的编译时候开辟空间的方式就不能够被满足。
这个就好比就是我想要在一个程序运行当中当中输入两百个同学的信息,此时我用数组分配了1000个同学的信息。此时我就会浪费很多的内存空间。当我想输入2000个同学信息的时候,此时数组分配的1000个同学信息存放不下去。
那么这个时候我们就可以使用动态内存开辟就可以完美的解决这个问题了。
什么是动态内存开辟?
所谓动态内存分配(Dynamic Memory Allocation)就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。
那么既然说到这个再来说说栈和堆的概念n:↓这样更加方便且容易理解。
在执行函数的时候,函数内部局部变量的存储单元都是可以在栈上进行创建的,函数执行结束的时候这些存储单元会被自动的进行释放。栈区主要存放运行函数所分配的局部变量,函数的参数,返回数据,返回地址等。
通常定义变量(或对象),编译器在编译时都可以根据该变量(或对象)的类型知道所需内存空间的大小,从而系统在适当的时候为他们分配确定的存储空间。这种内存分配称为静态存储分配;有些操作对象只在程序运行时才能确定,这样编译时就无法为他们预定存储空间,只能在程序运行时,系统根据运行时的要求进行内存分配,这种方法称为动态存储分配。所有动态存储分配都在堆区中进行。
当程序运行到需要一个动态分配的变量或对象时,必须向系统申请取得堆中的一块所需大小的存储空间,用于存贮该变量或对象。当不再使用该变量或对象时,也就是它的生命结束时,要显式释放它所占用的存贮空间,这样系统就能对该堆空间进行再次分配,做到重复使用有限的资源。
接下来介绍下动态内存开辟的函数的使用,知道如何用了,那么就相当于学会了这个动态内存开辟。
malloc()函数的声明,如下所示↓
void* malloc (size_t size);
分配一个大小为字节的内存块,返回一个指向该块开头的指针。
新分配的内存块的内容没有初始化,剩下不确定的值。
如果size为0,则返回值取决于特定的库实现(它可能是一个空指针,也可能不是),但返回的指针不应被解引用。
size→内存块的大小,以字节为单位。
- Size_t是一个无符号整型类型。
成功时,指向函数分配的内存块的指针。
该指针的类型始终是void*,可以将其转换为所需类型的数据指针,以便可解引用。
如果函数分配请求的内存块失败,则返回一个空指针NULL。
使用 malloc() 函数需要引头文件 #include
free()函数的声明,如下所示↓
void free (void* ptr);
ptr → 指针指向一个要释放内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果传递的参数是一个空指针,则不会执行任何动作。
该函数不返回任何值。
实际上 free() 函数是专门用来作动态内存的释放或者回收的。注意:free()当中的 ptr 是指向栈区的空间的,如果你存放堆区空间的话是属于未定义的。
当我们回收完成之后,最好把 ptr 当中的参数置成为 NULL,这样它就找不到那块原来的空间了。
使用 free() 函数需要引头文件 #include
示例代码如下所示↓
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
int main(void)
{
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
perror("main:");//perror() - 打印错误信息
}
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;//初始化赋值
printf("%d\n", p[i]);//相当于数组打印元素
}
//回收内存空间
free(p);
p = NULL;
return 0;
}
运行结果如下↓
0 1 2 3 4 5 6 7 8 9
有没有发现其实和数组的访问是一模一样的,只不过一个是在栈区上,这个是在堆区上。
那如果是 malloc() 函数返回的是空指针的情况n,我们只需要把malloc()内存块空间改变下即可。如下↓
int* p = (int*)malloc(1000000000000000 * sizeof(int));
那么此时的运行结果就是→Not enough space(没有足够的空位)是实际上也就是内存空间。
calloc()函数的声明,如下所示↓
void* calloc (size_t num, size_t size);
为num元素数组分配一块内存,每个元素大小为字节长,并将其所有位初始化为零。有效的结果是分配一个(num*size)字节的零初始化内存块。
如果size为0,则返回值取决于特定的库实现(它可能是一个空指针,也可能不是),但返回的指针不应被解引用。
- 参数
num → 要分配的元素数量。
size → 每个元素的大小。
size_t → 是无符号整型。
- 返回值
成功时,指向函数分配的内存块的指针。
该指针的类型始终是void*,可以将其转换为所需类型的数据指针,以便可解引用。
如果函数分配请求的内存块失败,则返回一个空指针。
calloc()函数和malloc()函数最大的区别无非就是:calloc()比malloc()就是参数多了一个,以及calloc()的函数会把每个字节进行初始化为全0,而malloc()不会对每个字节的内容进行初始化。calloc()会在返回地址之前把申请的空间的每个字节初始化全0的。
示例代码如下所示↓
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
int main(void)
{
int* p = (int*)calloc(10,sizeof(int));
if (p == NULL)
{
perror("main:");//perror() - 打印错误信息
}
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d\n", p[i]);//相当于数组打印元素
}
//回收内存空间
free(p);
p = NULL;
return 0;
}
运行结果如下↓
0 0 0 0 0 0 0 0 0 0
从这里我们可以发现,calloc()函数是帮我们进行了初始化为0的。那么如果我们把这里的代码改成是malloc()函数的形式看下会怎么样,其它不变来康康运行结果。
int* p = (int*)malloc(10 * sizeof(int));
-842150451 → ⑩行
从这里我们可以知道 malloc() 函数并没有帮我们进行初始化。
realloc()函数的声明,如下所示↓
void* realloc (void* ptr, size_t size);
功能→改变由ptr指向的内存块的大小。
- 参数
ptr → 是要调整内存的地址。
size → 内存块的新大小,以字节为单位。调整之后新的大小。
size_t → 是无符号整型。
返回值→一个指向重新分配的内存块的指针,它可以与ptr相同,也可以是一个新位置。
该指针的类型为void*,可以将其转换为所需的数据指针类型,以便可解引用。
注意→如果ptr是一个空指针,该函数的行为类似于malloc(),分配一个大小为字节的新块,并返回指向其开头的指针。 如下代码所示↓
int* p = (int*)malloc(10 * sizeof(int));
int* p = (int*)realloc(NULL,10 * sizeof(int));
实际上相当于等效。
示例代码如下所示↓
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
int main(void)
{
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
perror("main:");//perror() - 打印错误信息
}
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
//这里需要p的指向空间更大,增加20个整形类型。realloc() - 调整空间
int* ptr = (int*)realloc(p, 20 * sizeof(int));
if (ptr != NULL)
{
p = ptr;
}
//回收内存空间
free(p);
p = NULL;
return 0;
}
根据调试查看是否 realloc() 调整了 p 的内存空间。
Ⅰ→ 对NULL指针的解引用操作。
Ⅱ → 对动态开辟空间的越界访问。
Ⅲ → 使用 free() 释放非动态开辟的空间。
Ⅳ → 使用 free() 释放动态内存中的一部份。
Ⅴ → 对同一块动态开辟的空间,用 free() 进行多次的释放。
Ⅵ → 动态开辟的空间忘记释放会导致内存的泄露,是比较严重的。