0
点赞
收藏
分享

微信扫一扫

动态内存管理学习整理

我是小小懒 2022-04-05 阅读 42
c语言

目录

前言:

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

2. 动态内存函数的介绍

    2.1 malloc和free

    2.1.1 malloc空间创建成功:

    2.1.2 malloc空间创建失败:

    2.1.3 free的使用与注意点

        free的总要性:

    2.2 calloc

    2.3 realloc 

        realloc在调整内存空间的是存在两种情况:

3. 常见的动态内存错误

    3.1 对NULL指针的解引用操作

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

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

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

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

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


前言:

本博客涉及:

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

2. 动态内存函数的介绍

  • malloc
  • free
  • calloc
  • realloc 

3. 常见的动态内存错误


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

        经过之前的学习我们知道,在C语言中,我们所使用的程序语言的初始化是一经固定,就无法改变的:

int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间

        这样对于一个所需要的空间,只要开辟的够大就可以使用,但是上述的开辟空间的方式有两个特点:

  1. 空间开辟大小是固定的。
  2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。

        但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道, 那数组的编译时开辟空间的方式就不能满足了。 这时候就只能试试动态存开辟了。这也是,之所以会有动态内存管理的说法了。

2. 动态内存函数的介绍

(这里我们需要注意一个问题,就是动态内存管理(malloc,free,calloc,realloc)都是对内存的堆区区域进行操作的。)

    2.1 malloc和free

         所以,对于malloc函数我们需要注意:

  • 如果开辟成功,则返回一个指向开辟好空间的指针,需要利用指针进行接收。
  • 如果开辟失败,则返回一个NULL指针,进行接收后就会是一个NULL的指针,直接访问会导致程序发生错误,因此malloc的返回值一定要做检查。
  • 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。

        此外:

                如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。不能说这个做法是错的,只能说是不标准的,而且没有知道他会发生什么……。

    2.1.1 malloc空间创建成功:

#include <stdlib.h>

int main()
{
	int* p = (int*)malloc(40);
	if (NULL == p)
	{
		perror("malloc"); //打印NULL的错误原因
	}
	for (int i = 0; i < 10; i++)
	{
		*p = i;
		p++;
	}
	return 0;
}

    2.1.2 malloc空间创建失败:

        例如:当你申请的空间过于的庞大的时候,就会因为空间不足,导致的无法正常开辟动态内存空间。

    2.1.3 free的使用与注意点

         既然,现在我们懂得了如何进行申请,那申请了不用了,也应该要还了,所以我们来讲讲,如何还动态内存。(利用free函数)

#include <stdlib.h>

int main()
{
	int* ptr = (int*)malloc(40);
	int* p = ptr;
	if (NULL == p)
	{
		perror("malloc");
	}
	for (int i = 0; i < 10; i++)
	{
		*p = i;
		p++;
	}

	free(ptr);
	p = NULL;
	
    //if (p != NULL)
	//{
	//	*p = 1;
	//}

	return 0;
}

使用free所需要注意的知识点

  1.如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。

  2.如果参数 ptr 是NULL指针,则函数什么事都不做。

       3. free所释放的动态空间需要在动态内存申请的初始点,不然会导致程序崩溃。所以,我们需要先行将开辟的初始点地址进行记录,防止使用时将其改变了位置。

       4. free在将内存归还于编译器后,并不会将我们之前所传输的地址进行更改,所以,我们还拿着起初的动态内存地址,但是,要知道当我们拿着不是我们所拥有的地址,不慎将其进行使用,这是十分危险的行为,为导致程序的崩溃,所以我们再归还后最好将地址改为NULL,这样在后期如若再想使用。只需进行判断其是否为NULL即可。

        free的总要性:

        我们要知道,free函数进行内存释放可不是用来释放的玩的,要知道,作为动态内存不是与我们平时随手创建的变量一样,他是程序结束才会自行归还的,可想而知,对于一个长时间自身循环的程序,来来回回的申请程序又不会进行自行回收,就会导致内存泄漏的问题

    2.2 calloc

        其实calloc与mallloc差不多,相对于malloc来说,就是其多了一个功能,初始化数据为0;所以注意点也与malloc不尽相同。但是,同时因为calloc可以进行数据的初始化,而对于初始化,是需要进行地址的访问的,所以需要进行操作的步长的规定。

    2.3 realloc 

        有时候我们会发现,空间很有可能会申请的空间太大,或者太小,所以就需要一个可以改变的方式那 realloc 函数就可以做到对动态开辟内存大小的调整。(realloc函数的出现让动态内存管

理更加灵活)

#include <stdlib.h>
#include <stdio.h>


int main()
{
	int* ptr = (int*)malloc(40);
	int* p = ptr;
	if (NULL == p)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		*p = i;
		p++;
	}

	//当我们需要再扩大10个元素时
	p = realloc(ptr, 80);
	if (NULL == p)
	{
		perror("realloc");
		return 1;
	}
	ptr = p;
    
    //使用

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

        realloc在调整内存空间的是存在两种情况:

情况1 : 要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。


情况2 : 原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续

空间来使用。这样函数返回的是一个新的内存地址。

注意: realloc的申请空间也不是一定成功的,当失败的时候,会返回一个NULL的指针,所以,对于此点我们尤为要注意,不要直接就用原动态内存的指针直接接收,这样会丢失原地址,导致动态内存无法查找,导致无法free掉,从而内存泄漏。

3. 常见的动态内存错误

    3.1 NULL指针的解引用操作


void test()
{
	int* p = (int*)malloc(INT_MAX / 4);
	*p = 20;//如果p的值是NULL,就会有问题
	free(p);
}

        也就是我们之前所强调的一点,malloc,calloc,realloc,如果实施失败就会返回NULL,如果我们忘记进行判断后再使用,就会出现这个问题。

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


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);
}

         与在栈区上开辟空间一样,即使实在堆上开辟空间的动态内存开辟,程序员只能在已申请的空间内进行操作,如若超过,都会导致野指针的问题,导致程序崩溃。

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

void test()
{
	int a = 10;
	int* p = &a;
	free(p); //err
}

        正如我们前面所说,这是free未进行定义的操作,本质上来说:因为,指针p指向的空间不是我们所申请的,是a这个变量在栈上的空间。我们没有动态去申请空间, 那么就没有理由释放空间。

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

void test()
{
	int* p = (int*)malloc(100);
	p++;
	free(p);//p不再指向动态内存的起始位置
}

        需要注意:free需要对应动态内存的起始位置,如若free,p不再指向动态内存的起始位置,那么一free使用只会程序崩溃。

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

void test()
{
	int* p = (int*)malloc(100);
	free(p);
	free(p);//重复释放
}

        重复的free会导致程序的崩溃。

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

#include <stdlib.h>

void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}

int main()
{
	test();
	while (1);
}

        这也就是我们之前说提到的问题,如若不处理,就会导致内存泄漏,导致内存耗尽。

举报

相关推荐

0 条评论