【C语言】动态内存管理

本文详细介绍了C语言中动态内存管理的原理和函数,包括malloc、calloc、realloc和free的使用,强调了动态内存分配的必要性和可能导致的问题。特别提到了realloc在扩容时的三种情况及使用注意事项,以及如何避免内存泄漏和处理野指针。此外,还讨论了返回栈区地址的问题,指出返回栈区内存地址可能导致的未定义行为。
摘要由CSDN通过智能技术生成

动态内存分配存在的原因:

        节省内存空间。若预先开辟一块空间,小了导致不够用数组越界,大了用不完造成内存空间浪费。

malloc函数、calloc函数与free函数

malloc

#include <stdlib.h>

void* malloc (size_t size)  
//void*方便后续使用 需要什么类型的指针 强制类型转换一下即可
//size的大小是字节的大小

//例如要用malloc模拟int arr[10] = {0}
int* p = (int *) malloc(40);

calloc

#include <stdlib.h>

void* calloc (size_t num, size_t size)  
//num表示元素个数
//size表示每个元素的大小

//例如要用calloc模拟int arr[10] = {0}
int* p = (int *) calloc(10,sizeof(int));

        对malloc,若开辟成功,函数返回一个void*指针;若因为堆内存空间不足开辟失败,函数返回NULL。      

        malloc和calloc函数的区别一个是参数不一样,另一个是malloc不会初始化数组,而calloc会初始化数组元素为0。所以直接返回地址的malloc效率会更高一些。

realloc

#include <stdlib.h>

void* realloc(void* ptr, size_t size);
//ptr表示一个由malloc, calloc, realloc开辟的指针
//若ptr是空指针 那realloc和malloc的作用是相同的

        realloc的作用是扩容,但是扩容会有几种不同的情况发生。

Ⅰ:在堆中,ptr后面有充足的空间,则顺利扩容,返回的指针值和ptr相同

Ⅱ:若ptr的直接后面没有足够的目标空间开辟,即后面的空间可能已经被别的数据占用,则函数会在另一个位置重新开辟完整的空间,返回的指针值就和ptr不同,是一个全新的地址,同时原来ptr代表的一块内存空间也被释放销毁

Ⅲ:若整个堆中都找不到足够的空间,函数会返回空指针,开辟失败

        针对情况Ⅲ,使用realloc函数时候要注意:

        ①不直接用函数返回的指针赋值给原来的指针,因为要是开辟失败返回空指针,那原来的地址也找不到了

        ②对返回的指针进行判断,不是空指针再继续使用 

int* ptr = (int*) malloc(100);
int* tmp = (int*) realloc(ptr, 1000);

if (NULL != tmp)
    ptr = tmp;

free

        内存开辟后,若使用完毕了最好还是能够回收内存,就需要用上free函数

#include <stdlib.h>

void free (void* p)

        注意!malloc函数得到的指针最开始是存有一个堆中的地址的,free之后,原来堆中被占用的内存可以给别处用了,但是指针存的地址还在,这就导致形成了一个野指针,所以需要把指针赋一个NULL,这样他就无法再次被使用了。

        有一个感觉没啥用的函数strerror(errno)

#include <stdlib.h>
#include <errno.h>  //需要头文件
#include <string.h>

int main()
{
	int* arr = (int*)malloc(40000000000000000);
	if (NULL == arr)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}

    free (arr);
    arr = NULL;

	return 0;
}

//输出Not enough space

常见的动态内存问题

对空指针NULL解引用

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

对非动态内存进行free释放        

使用free释放内存的一部分

没有对动态内存释放导致内存泄漏

函数返回栈区地址问题

        探究这个问题之前我们还是先来复习一下内存的区域功能划分,如图: 

        临时的数据都是再栈上开辟的,而动态内存时在堆区开辟的。两者的区别在于栈区的数据会在局部函数结束后被销毁,比如说形参,局部函数中的变量,而堆区则会在程序执行结束后,或者主动释放后才销毁回收。这两者的区别就造成了返回栈区地址的问题。

        常见错误的代码如下:

char* GetMemory(void)
{
    char p[] = "hello world";
    return p;
}
void Test(void)
{
    char* str = NULL;
    str = GetMemory();
    printf(str);
}

        p确实存储了字符数组p的首元素地址,但是在GetMemory函数调用结束后,p[]所在的栈空间就被销毁回收了,字符数组的内容不复存在,返回值p代表的指针也成了空指针。

        又如:

int* test(int* p)
{
	int a = 10;
	return &a;
}
int main()
{
	int* i = 0;
    i= test(&i);
	printf("%d", *i);
    //输出10
	return 0;
}

        这里同样时返回了栈区地址,为什么可以正确得到10的结果?

        因为栈空间确实已经释放,但是原来10的空间暂时还没有被其他数据替换、占用,所以10的值并没有改变。假若在printf("%d", *i);前一行打印任何东西,就可以发现不再能输出结果10。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值