上篇文章介绍了枚举,联合相关的内容,大家可以点击链接进行浏览:c语言进阶部分详解(详细解析自定义类型——枚举,联合(共用体))-CSDN博客
各种源码大家可以去我的github主页进行查找:Nerosts/just-a-try: 学习c语言的过程、真 (github.com)
今天来介绍动态内存管理 的相关内容:
目录
一.为什么存在动态内存分配
二.动态内存函数的介绍
2.1 malloc( )函数
2.2free( )函数
2.3calloc()函数
2.4realloc()函数
三.常见的动态内存错误
3.1对NULL指针的解引用操作
3.2对动态开辟空间的越界访问
3.3对非动态开辟内存使用free释放
3.4使用free释放一块动态开辟内存的一部分
3.5对同一块动态内存多次释放
3.6动态开辟内存忘记释放(内存泄漏)
四.C/C++程序的内存开辟
我们熟悉的内存开辟方法:
int a = 20;//在栈空间上开辟四个字节的空间
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
但是,有时候我们需 要的空间大小在程序运行的时候才能知道 , 那数组的编译时开辟空间的方式就不能满足了。这时候就只能试试动态存开辟了
各种变量存储位置:
malloc函数是C语言中的一个动态内存分配函数,用于在程序运行时动态地分配内存空间。它的作用是在堆区中申请一块指定大小的内存空间,并返回该内存块的首地址。
malloc函数的原型为:
#include
void *malloc(size_t size)
其中,size_t是一个无符号整数类型,用于表示要分配的内存空间的大小(单位为字节)。malloc函数返回一个void类型的指针,指向分配的内存空间的起始地址。如果分配失败,则返回NULL
free函数是C语言中的一个内存释放函数,用于释放之前使用malloc、calloc或realloc函数分配的内存空间。它的作用是将不再使用的内存空间返回给系统,以便其他程序或操作系统可以重新利用该内存
free函数的原型为:
#include
void free(void *ptr);
ptr是一个指向要释放的内存空间的指针。该指针必须是之前使用malloc、calloc或realloc函数返回的指针,或者是NULL指针。如果ptr是NULL指针,则free函数不会进行任何操作
示例:
int main()
{
int* arr = (int*)malloc(sizeof(int) * 10);//使用malloc函数进行动态内存开辟
//因为返回值是void* 所以要强转一下
if (arr == NULL)
{
perror("malloc");//如果开辟失败就进行说明
}
for (int i = 0; i < 10; i++)
{
arr[i] = i;
}
free(arr);//在程序最后要结束时主动进行释放
arr=NULL;
return 0;
}
calloc函数是C语言中的一个内存分配函数,用于在堆上分配一块指定大小的内存空间,并将该空间的每个字节初始化为0
calloc函数的原型为:
#include
void *calloc(size_t num, size_t size);
其中,num表示要分配的元素个数,size表示每个元素的大小。calloc函数会返回一个指向分配内存的指针,如果分配失败则返回NULL
int main()
{
int* p = (int*)calloc(10, sizeof(int));
if (NULL != p)
{
perror("calloc");
}
//使用。。。。
free(p);
p = NULL;
return 0;
}
大家可以看到内存情况:
#include
void *realloc(void *ptr, size_t size);
其中,ptr是之前分配的内存指针,size是重新分配的内存大小。realloc函数会尝试将ptr指向的内存空间重新分配为size大小的内存空间,并返回一个指向重新分配后的内存空间的指针
扩展空间情况也有两种:
int main()
{
int* p = (int*)calloc(10, sizeof(int));
if (NULL != p)
{
perror("calloc");
}
for (int i = 0; i < 10; i++)
{
p[i] = i;
}
//使用。。。。
int* pa = NULL;
pa = realloc(p, 1000);
if (p != NULL)
{
p = pa;
}
free(p);
p = NULL;
return 0;
}
void test1()
{
int *p = (int *)malloc(sizeof(int)*10);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
void test2()
{
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(iny i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}
void test3()
{
int a = 10;
int *p = &a;
free(p);//这也是不可以的
}
void test4()
{
int *p = (int *)malloc(100);
p++; //p不再指向动态内存的起始位置
free(p);
}
void test5()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();//调用函数后,p的那片空间没办法用了,在函数结束后,函数内动态分配的内存空间不会自动销毁
while(1);
}
在函数结束后,函数内动态分配的内存空间不会自动销毁。这是因为动态分配的内存空间是在堆上分配的,而不是在函数的栈帧上。栈帧上的局部变量在函数结束时会自动销毁,但堆上分配的内存空间需要手动释放 。
C/C++程序内存分配的几个区域:
1. 栈区( stack) :在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执结束时这些 存储单元自动被释放 。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等2. 堆区( heap ):一般由 程序员分配释放 , 若程序员不释放,程序结束时可能由 OS 回收 。分配方式类似于链表3. 数据段(静态区)( static )存放全局变量、静态数据。 程序结束后由系统释放 。4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码
好啦,这次的内容就先到这里的,下一次会讲解一些关于动态内存的经典的题目和柔性数组相关的知识。