当前位置: 首页 > article >正文

【C语言进阶】- 动态内存管理

动态内存管理

    • 1.1 为什么存在动态内存分配
    • 1.2 动态内存函数介绍
    • 2.1 malloc函数的使用
    • 2.2 free函数的使用
    • 2.3 calloc函数的使用
    • 2.4 realloc函数的使用
    • 3.1 常见的动态内存错误
    • 3.2 常见笔试题

1.1 为什么存在动态内存分配

我们已经掌握的内存开辟方式有:
int val = 20;//在栈空间上开辟四个字节
char arr[10] = { 0 };//在栈空间上开辟10个字节的连续空间
但是上述的开辟空间的方式有两个特点:
1. 空间开辟大小是固定的。
2. 数组在声明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,
那数组的编译时开辟空间的方式就不能满足了。
这时,就需要动态开辟内存了...

1.2 动态内存函数介绍

开辟内存

void* malloc (size_t size);

malloc函数是在堆中连续开辟size个字节的空间,返回值为空间的起始地址,开辟失败返回NULL指针

释放内存

void free (void* ptr);

如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
如果参数 ptr 是NULL指针,则函数什么事都不做。

2.1 malloc函数的使用

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

/*
	void* malloc( size_t size );
*/

int main()
{
	int arr[10] = { 0 };
	// 动态内存开辟
	int* p = (int*)malloc(40); // 开辟40个字节
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1; // 给main函数返回1表示存在问题
	}

	// 使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}

	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}

	free(p);  // 释放内存空间,不然会内存泄漏,可以通过调试窗口 p,10 来观看数据
	p = NULL; // 防止野指针,因为它指向的空间已经释放了,已经还给操作系统了,p如果不置空的话,这时候还记得地址
	return 0;
}

还未free时的内存状态
在这里插入图片描述

free后的内存状况
在这里插入图片描述

p=NULL 是为了防止野指针,因为它指向的空间已经释放了,已经还给操作系统了,p如果不置空的话,这时候还记得地址,如果访问的话就非法访问内存了

2.2 free函数的使用

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int a = 10;
	int* p = &a; // 不是动态开辟的
	free(p);
	p = NULL;

	int* p2 = NULL; // 什么事都不做
	free(p2);
	return 0;
}

free函数是只能释放动态内存的,不是动态开辟的会报错

在这里插入图片描述

2.3 calloc函数的使用

void* calloc (size_t num, size_t size);

函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

#include  <stdio.h>
#include  <stdlib.h>
#include  <errno.h>
#include  <string.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		printf("%s\n",strerror(errno));
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++) {
		printf("%d ",*(p+i));
	}
	// 释放堆中的内存
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

2.4 realloc函数的使用

void* realloc (void* ptr, size_t size);
#include  <stdio.h>
#include  <stdlib.h>
#include  <errno.h>
#include  <string.h>
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}

	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}

	// 扩容
	// 要用新的指针来接收并且判断,否则,如果扩容失败会变成野指针,本来指向40个字节,扩容失败突然指向NULL
	int* ptr = (int*)realloc(p, 80); 
	if (ptr != NULL)
	{
		p = ptr;

	}

	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	free(p);
	p = NULL;

	return 0;
}

在这里插入图片描述

需要注意的事,realloc在开辟内存空间时,存在2种情况

在这里插入图片描述

malloc和realloc函数的关联


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

int main()
{
	int* ptr = (int*)realloc(NULL, 40); // 相当于 malloc(40)
	return 0;
}

3.1 常见的动态内存错误

1.对NULL指针的解引用操作

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

int main()
{
	int* p = (int*)malloc(40);
	if (p = NULL) // err
	{
		return 1;
	}
	*p = 20;
	free(p);
	p = NULL;
	return 0;
}

这里如果动态开辟内存失败,p为NULL,对NULL指针进行访问就会有问题

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

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)malloc(40);
	if (p = NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	int i = 0;
	for (i = 0; i <= 10; i++) // 越界了
	{
		p[i] = i;
	}
	free(p);
	p = NULL;
	return 0;
}

3. 对非动态开辟内存使用free函数

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

int main()
{
	int a = 10;
	int* p = &a;
	free(p);
	p = NULL;
	return 0;
}

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

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

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		return 1;
	}

	int i = 0;
	for (i = 0; i < 10; i++)
	{
		 // 这里p的位置被改变了,free(p)时,只释放掉了部分内存
		*p = i;
		p++;
	}

	free(p);
	p = NULL; 

	return 0;
}

5. 对同一块空间的多次释放

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

int main()
{
	int* p = (int*)malloc(40);

	free(p);
	p = NULL; 
	free(p);


	return 0;
}

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

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


void test()
{
	int* p = (int*)malloc(100);
	int flag = 0;
	scanf("%d",&flag);

	if (flag == 5)
	{
		return;
	}

	free(p);
	p = NULL;
}

int main()
{
	test();
	return 0;
}

当flag为5时,就不会释放内存

3.2 常见笔试题

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

void GetMemory(char* p) // 堆中的p
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL; // 栈中的p
	GetMemory(str); // 传递的是null,不是str的地址
	strcpy(str, "hello world"); // 将str给到空指针
	printf(str);
}

int main()
{
	Test();
	return 0;
}

str为NULL,传递给GetMemory函数,GetMemory函数里的p是堆中的所开辟的p,然后给它开辟空间,然后返回到Test函数,这时的str依旧为NULL,然后调用strcpy函数,是把"hello world"给到NULL指针,会出现错误。

#include <stdio.h>

char* GetMemory(void)
{
	char p[] = "hello world"; // 这里是局部变量,放置在栈中, 函数执行完就被销毁了,还给操作系统了
	//char* p = "hello world"; 
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory(); // 野指针,非法访问内存了
	printf(str);
}


int main()
{
	Test();
	return 0;
}

这里GetMemory后,返回的是局部变量的地址,放在栈中,但是这个函数执行完后这个变量就被销毁了,所以再通过指针访问这块内存空间就是非法访问内存了。

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

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
	
	
	// 下列为修改后的代码
	//free(str);
	//str = NULL;
}


int main()
{
	Test();
	return 0;
}

这个很容易,动态开辟内存后未能后free

在这里插入图片描述


http://www.kler.cn/a/526699.html

相关文章:

  • 【外文原版书阅读】《机器学习前置知识》2.用看电影推荐的例子带你深入了解向量点积在机器学习的作用
  • 商密测评题库详解:商用密码应用安全性评估从业人员考核题库详细解析(8)
  • LINUX部署微服务项目步骤
  • 【Go语言圣经】第五节:函数
  • QT 通过ODBC连接数据库的好方法:
  • Excel - Binary和Text两种Compare方法
  • 【memgpt】letta 课程5:可编程的agent内存
  • [HOT 100] 0003. 无重复字符的最长子串
  • 本地AI模型:未来智能设备的核心驱动力
  • Brave132 编译指南 Windows 篇:构建与运行(七)
  • Python3 【集合】:使用示例参考手册
  • 电感的饱和、温升、额定电流
  • Protocol Buffers c# with c++ communcation demo
  • 编程题-三数之和(中等)
  • 20-30 五子棋游戏
  • 【2024年华为OD机试】 (B卷,100分)- 乘坐保密电梯(JavaScriptJava PythonC/C++)
  • 如何用大语言模型做一个Html+CSS+JS的词卡网站
  • WINDOWS安装eiseg遇到的问题和解决方法
  • day1-->day7| 机器学习(吴恩达)学习笔记
  • FLTK - FLTK1.4.1 - 搭建模板,将FLTK自带的实现搬过来做实验
  • 知识管理平台在数字经济时代推动企业智慧决策与知识赋能的路径分析
  • 全面认识了解DeepSeek+利用ollama在本地部署、使用和体验deepseek-r1大模型
  • 【仓颉】仓颉编程语言Windows安装指南 配置环境变量 最简单解决中文乱码问题和其他解决方案大全
  • 360嵌入式开发面试题及参考答案
  • 【Linux指令/信号总结】粘滞位 重定向 系统调用 信号产生 信号处理
  • 【开源免费】基于Vue和SpringBoot的医院资源管理系统(附论文)