动态内存管理

1.为什么存在动态内存分配

int i=0;
int arr[20]={0};
  • 上述代码开辟空间大小是固定的
  • 有时候我们要再程序运行的时候才能知道具体需要多少空间

2.动态内存函数介绍

1.malloc

void* malloc (size_t size);

这个函数向内存申请一块连续的空间,并返回这块地址的指针

  • 申请成功,返回一个开辟好空间的指针
  • 申请失败,返回NULL 
  • sizeof(0):未定义行为,具体由编译器决定
  • 返回值由void *接收,具体使用交给操作者

2.free

void free (void* ptr);

作用是释放由malloc、calloc、realloc开辟的内存

  • 如果指针ptr指向的内存不是有动态内存函数开辟的,那么free(ptr)是未定义行为,取决于编译器
  • 如果是free(NULL),则什么也不做
  • 该函数没有返回值

3.calloc 

void* calloc (size_t num, size_t size);

函数的功能是Allocate and zero-initialize array,也就是分配和零初始化数组

  • 它会开辟一个num*size的空间,并且把每个元素的值都初始化成0
  • size如果是0,那么返回值是未定义的,可能是NULL,也可能不是,但是都不能被解引用
  • 它和malloc的区别就是将内存空间的每个字节都初始化成了0 
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdlib.h>
int main()
{
	int* ptr = (int*)malloc(sizeof(int) * 10);
	int* p = (int*)calloc(10, sizeof(int));
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		ptr[i] = i + 1;
		p[i] = i + 1;
	}


	free(ptr);
	ptr = NULL;
	free(p);
	p = NULL;
 	return 0;
}

 

我们可以看到malloc没有初始化,calloc初始化了

4.realloc

void* realloc (void* ptr, size_t size);

该函数的功能是Reallocate memory block ,也就是重新分配内存块

  • 只能重新分配malloc和calloc开辟的空间
  • size为0属于未定义行为
  • ptr是需要调整的内存地址,size是需要开辟的字节大小
  • void*用来接收重新分配内存块的地址
  • 如果ptr==NULL,就实现了malloc的功能

分两种情况

第一种情况,指针在后面重新找一块足够大的地址,将原来的数据拷贝过去

第二种情况,后面的空间足够,返回原来的指针

  •  开辟失败则返回NULL

所以我们我们一般这样操作

int main()
{
	int* ptr = calloc(10, sizeof(int));
	int *p=realloc(ptr, sizeof(int) * 15);
	//避免后面返回空指针然后解引用这种错误
	if (p != NULL)
	{
		ptr = p;
	}


	free(ptr);
	ptr = NULL;
	return 0;
}

3. 常见的动态内存错误

3.1 对NULL指针的解引用操作

void test1()
{
	int* ptr = malloc(INT_MAX);
	*ptr = 1;
}

3.2 对动态开辟空间的越界访问

void test2()
{
	int* ptr = calloc(5, sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", ptr[i]);
	}
}

3.3 对非动态开辟内存使用free释放

void test3()
{
	int i = 0;
	int* ptr = &i;
	free(ptr);
	ptr = NULL;
}

3.4 使用free释放一块动态开辟内存的一部分

void test4()
{
	int* ptr = malloc(sizeof(int));
	ptr++;
	free(ptr);
	ptr = NULL;
}

3.5 对同一块动态内存多次释放

void test5()
{
	int* ptr = calloc(10, sizeof(char));
	free(ptr);
	free(ptr);
}

3.6 动态开辟内存忘记释放(内存泄漏)

void test6()
{
	int* ptr = (int *)calloc(10, sizeof(int));
	if (NULL != ptr)
	{
		*ptr = 10;
	}
}

系统不会报错,但是会导致系统内存越来越小,就是我们所说的内存泄漏

4.柔性数组

4.1什么是柔性数组?

struct Node
{
	int i;
	char str[0];
};

或者这个

struct Node
{
	int i;
	char str[];
};

特点:

  • 柔性数组前面必须有一个成员变量
  • sizeof(结构体类型)不包括柔性数组的大小
  • 用动态函数来维护或者说开辟柔性数组的空间

4.2柔性数组的使用

//代码1
typedef struct Node
{
	int i;
	char str[];
}Node;

int main()
{
	Node *n= (Node*)malloc(sizeof(Node)+20);
	n->i = 100;
	strcpy(n->str, "hello world");
	printf("%d %s", n->i, n->str);
	return 0;
}

4.3柔性数组的优势

上述代码我们其实可以自己写

typedef struct Node
{
	int i;
	int *str;
}Node;

int main()
{
	Node* n = (Node*)malloc(sizeof(Node));
	if (n == NULL)
	{
		perror("malloc");
		return 1;
	}
	n->i = 100;
	int* p = (int*)malloc(sizeof(int) * 20);
	if (p == NULL)
	{
		free(n);
		n = NULL;
		perror("malloc");
		return 1;
	}
	n->str = p;
	int i = 0;
	for (i = 0; i < 20; i++)
	{
		(n->str)[i] = i + 1;
	}
	for (i = 0; i < 20; i++)
	{
		printf("%d ", (n->str)[i]);
	}
	free(n->str);
	n->str = NULL;
	free(n);
	n = NULL;

	return 0;
}

1.方便内存释放

我们的代码做了二次内存分配,当需要free这段结构体空间的时候,别人并不知道结构体内容也需要释放,而且这里必须先释放结构体变量的内存分配,再释放整个结构体

2.有利于访问速度

连续的空间有利于访问速度,而且柔性数组使用了一整块内存空间,我们的代码则使用了两块不连续空间,就有可能产生内存碎片,使我们无法有效利用内存空间了

  • 32
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值