程序员成长之旅——动态内存管理

为什么存在动态内存分配

我们已经掌握的内存开辟的方式有:

int hr = 20;//在栈上开辟四个字节的空间
char a[10] = {0};//在栈上开辟连续10个字节的空间,在编译的时候开辟

但是这么开辟还是有两大缺点

  • 开辟的大小是固定的
  • 如果是要在运行的时候在开辟空间的话,这样是不可以的

这时候我们就引入了动态内存开辟。

动态内存函数的介绍

malloc 和 free

C语言提供了一个动态内存开辟的函数:

void *malloc( size_t size );

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

  • 开辟成功的话,则返回一个指向开辟好空间的一个指针
  • 开辟失败的话,则返回空指针,因此我们要判断返回的指针是否为空
  • 返回值为void*,因此我们并不知道返回的类型是什么,具体使用的时候在决定
  • 如果size为0,那么malloc的行为是标准还是未定义的,取决于编译器

C语言提供了另一个函数,用来释放动态开辟的空间:

void free (void* ptr);

free函数用来释放动态内存开辟的内存

  • 如果ptr指向的空间不是动态内存开辟的,那么free是未定义的
  • 如果ptr = NULL,那么free是不做任何释放的

malloc 和 free都声明在 stdlib.h头文件中。举个例子:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
int main()
{
	////代码1
	//int num = 0;
	//scanf("%d", &num);
	//int arr[num] = { 0 };
	//代码2
	int* ptr = NULL;
	//ptr = (int*)malloc(10 * sizeof(int));
	ptr = (int*)malloc(INT_MAX);
	if (NULL == ptr)//判断开辟是否失败
	{
		printf("%s\n", strerror(errno));
		//perror("use malloc"); 不需要头文件  相对简单
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(ptr + i) = 0;
	}
	free(ptr);//释放ptr所指向的动态内存
	ptr = NULL;//是否有必要?  这个是要指向空的,因为free释放之后它还是指向那块空间,所以要置空

	return 0;
}

calloc

C语言还提供了一个函数为calloc,calloc也用来动态内存开辟。原型如下:

扫描二维码关注公众号,回复: 8624802 查看本文章
void* calloc(size_t num,size_t size);
  • 函数的功能是为num个大小为size的元素开辟一个空间,并且把每个空间的字节初始化为0。
  • 与malloc的区别只在于会初始化这个字节为0。

举个例子

int main()
{
	int* p = calloc(10, sizeof(int));
	if (NULL != p)
	{
		//使用空间
	}
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述
在有要求申请的空间字节必须初始化为0的时候我们就用calloc,但是没有要求的话,效率高的话就用malloc。

realloc

  • realloc函数的出现让动态内存开辟更加灵活
  • 有时候我们发现开辟的空间太小了,或者太大了,这时候用realloc就可以灵活调整这个内存空间。函数原型如下:
void* realloc(void* ptr,size_t size);
  • ptr是要调整的内存的初始地址
  • size是调整之后新字节大小
  • 返回值是调整之后的内存首地址
  • 这个函数调整之后还会将原数据移动到新的内存空间中
  • realloc在调整内存空间有两种情况
    情况1.原有空间之后有足够大的空间
    情况2.原有空间之后没有足够大的空间
    情况1 当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
    情况2 当是情况2 的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。 由于上述的两种情况,realloc函数的使用就要注意一些。
    举个例子:
int main()
{
	int* ptr = malloc(100);
	if (ptr != NULL)
	{
		//业务处理
	}
	else
	{
		exit(EXIT_FAILURE);
	}
	//扩展容量
	//代码1
	ptr = realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)
	//这样申请是有很大弊端的,假设申请失败,之前的数据也会消失
	//代码2
	int* p = NULL; p = realloc(ptr, 1000);
	if (p != NULL)
	{
		ptr = p;
	}
	//业务处理
	free(ptr);
	ptr = NULL;
	return 0;
}

realloc(NULL,10),像这样第一个传空指针的话,用法就类似于malloc。

常见的动态内存的错误

  • 对NULL指针的解引用操作
void test()
{
	int* p = (int*)malloc(INT_MAX / 4);
	*p = 20;//如果p的值是NULL,就会有问题
	free(p);
}
  • 对动态开辟空间的越界访问
void test()
{
	int i = 0;
	int* p = (int*)malloc(10 * sizeof(int));
	if (NULL == p)
	{
		exit(EXIT_FAILURE);
	}
	for (i = 0; i <= 10; i++)
	{
		*(p + i) = i;//当i是10的时候越界访问
	}
	free(p);
}
  • 对非动态开辟内存使用free释放
void test()
{
	int a = 10;
	int* p = &a;
	free(p);//ok?
}
  • 使用free释放动态开辟内存的一部分
void test()
{
	int* p = (int*)malloc(100);
	p++;
	free(p);//p不再指向动态内存的起始位置
}
  • 对同一块动态内存多次释放
void test()
{
	int* p = (int*)malloc(100);
	free(p);
	free(p);//重复释放
}
  • 动态开辟内存忘记释放(内存泄漏)
void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}
int main()
{
	test();
	while (1);
}

几个经典的笔试题

笔试题

int* f1(void)
{
	int x = 10;
	return (&x);
}
int main()
{
	int* p = f1();
	printf("%d\n", p);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
题目一

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello word");//在这崩溃
	printf(str);
}
int main()
{
	Test();
	return 0;
}

请问运行Test 函数会有什么样的结果?

str 和 p是不同的空间,而p开辟之后,出了空间会销毁,str还是NULL,所以会崩溃。
还有一处错误是内存泄漏。也就是没有free。

改正:
在这里插入图片描述

题目二

char* GetMemory(void) 
{
	char p[] = "hello world";//局部变量存在栈中,出函数就销毁
	return p;
}
void Test(void) 
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}
int main()
{
	Test();
	return 0;
}

请问运行Test 函数会有什么样的结果?

局部变量存在栈中,出函数就销毁

题目三

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void Test(void) {
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
}
int main()
{
	Test();
	return 0;
}

请问运行Test 函数会有什么样的结果?

free(str);
str = NULL;要加上,因为上面会有内存泄漏

题目四

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "word");
		printf(str);
	}
}
int main()
{
	Test();
	return 0;
}

请问运行Test 函数会有什么样的结果?

非法访问内存
free(str)后面应该加上 str = NULL

C/C++程序的内存开辟

在这里插入图片描述

发布了76 篇原创文章 · 获赞 16 · 访问量 4445

猜你喜欢

转载自blog.csdn.net/wuweiwuju___/article/details/99873995
今日推荐