⚡Hello~这里是傾城49⚡
🐏不定期更新知识干货~🐏
🌈点个关注不迷路~🌈
文章目录
前言
当我们要创建一个目录,写一些东西,开辟一定的空间,但是写过一次后就不能再写了,因为开辟空间已经满了。这时候就需要用到动态内存,随时增加或删除内存。
动态内存函数的介绍
malloc
void * malloc ( size_t size )
向内存申请一块连续可用的空间,申请完后返回指向这块空间的指针。
- malloc的返回值一定要检查是否为空指针。
- malloc申请的空间没有初始化,直接返回起始地址。
- 若参数size为0,则malloc的行为取决于编译器。
free
void * free ( void * ptr )
- 释放申请的内存
- 若参数ptr指向的空间不是动态开辟的,则free函数行为未定义
- 若参数ptr为空指针,则函数无意义
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(40);//放10个整型
if (p == NULL)
{
printf("%s\n", strerror(errno));
}
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i + 1;//存放1~10
}
//打印
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
free(p);
p = NULL;
return 0;
}
calloc
void * calloc ( size_t num, size_t size )
- num指开辟元素的个数。
- size指每个元素的大小。
- calloc申请的空间会把空间初始化为0,然后再返回起始地址。
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int main()
{
int* p = (int*)calloc(10, sizeof(int));
if (p == NULL)
{
perror("calloc");
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));//10个0
}
free(p);
p = NULL;
return 0;
}
realloc
void * realloc ( void * ptr, size_t size )
- realloc可调整开辟空间的大小。
- ptr为要调整的内存地址,size为调整之后的大小。
- 返回值为调整之后的内存起始位置。
- 若扩容空间时不够一个连续空间,则realloc会找一个满足空间大小的新的连续空间,旧空间的数据拷贝到新空间前面的位置。
- 用新的指针接收数据。
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(5 * sizeof(int));
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
int i = 0;
for (i = 0; i < 5; i++)
{
*(p + i) = i + 1;
}
//增加空间
int* ptr = (int*)realloc(p, 10 * sizeof(int));
if (ptr != NULL)
{
p = ptr;
}
//打印
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
free(p);
p = NULL;
return 0;
}
常见的错误
1.对NULL指针解引用
void test()
{
int *p = (int*)malloc(4);
*p = 20;//如果p的值是NULL,则会报错
free(p);
p = NULL;
}
2.对动态开辟空间的越界访问
void test()
{
int i = 0;
int *p = (int*)malloc(5*sizeof(int));
if(p == NULL)
{
return;
}
for(i=0; i<=5; i++)
{
*(p+i) = i;//当i是5时越界访问
}
free(p);
p = NULL;
}
3.对非动态开辟内存使用free
void test()
{
int a = 100;//栈区
int *p = &a;
free(p);
p = NULL;
}
4.使用free释放一块动态开辟空间
void test()
{
int *p = (int*)malloc(5 * sizeof(int));
int i = 0;
for(i=0; i<5; i++)
{
*p = i;
p++;
}
//循环完p的地址已经动了
free(p);//释放的是起始位置
p = NULL;
}
5对同一块动态内存多次释放
void test()
{
int *p = (int*)malloc(5 * sizeof(int));
if(p == NULL)
{
return;
}
free(p);
free(p);
//若忘记前面释放过,则会报错
//可在释放后加上p=NULL
//free(NULL)无影响
}
6.动态开辟内存忘记释放
void test()
{
int *p = (int*)malloc(5 * sizeof(int));
}
int main()
{
test();
//导致内存泄漏
}
C/C++程序中的内存开辟
1.栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运输内置于处理器的指令集中,效率高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
2.堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配方式类似于链表。
3.数据段(静态区)(static):存放全局变量、静态数据。程序结束后由系统释放。
4.代码段:存放函数体(类成员函数和全局函数)的二进制代码。
柔性数组
C99中,结构中的最后一个元素允许是未知大小的数组,叫做【柔性数组】成员
struct S
{
int a;
int b[0];//大小未知 - 柔性数组成员
};
特点
- 结构中的柔性数组成员前面必须至少有一个其他成员。
- sizeof返回的这种结构大小不包括柔性数组的内存。
- 包含柔性数组成员的结构用malloc函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
typedef struct S
{
int a;
int b[0];
}s;
printf("%d\n",sizeof(s));//4
使用
int i = 0;
s *p = (s*)malloc( sizeof(s) + 50 * sizeof(int) );
p->i = 100;
for(i=0; i<50; i++)
{
p->b[i] = i;
}
free(p);
p = NULL;
柔性数组成员b,获得了50个整型元素的连续空间。
总结
以上就是动态内存管理的相关知识点~