首页 > 系统相关 >高阶C语言之四:动态内存管理

高阶C语言之四:动态内存管理

时间:2024-11-18 16:43:38浏览次数:3  
标签:malloc str int void free C语言 动态内存 之四 NULL

目录

开辟和释放动态内存

malloc开辟空间

free释放空间

clloc初始化开辟空间

realloc修改动态内存

常见的内存错误

1、对空指针的解引用操作

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

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

4、使用free释放动态内存的一部分 

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

6、动态开辟内存忘记释放

动态内存的经典题目

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

 ​


指针+结构体+动态内存管理=数据结构

动态内存相关代码:month_11/test_14/main.c · Hera_Yc/bit_C_学习 - 码云 - 开源中国

动态项目实践:Project_5 · Hera_Yc/Project - 码云 - 开源中国

栈区、堆区、静态区存储的数据:


开辟和释放动态内存

头文件声明 <stdlib.h>

动态内存指的就是堆区内存,malloc和free都是建立在堆区的基础上的。

malloc开辟空间

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

void *malloc( size_t size );

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

  • 开辟成功,返回一个指向这片空间的指针。
  • 开辟失败,返回一个空指针。
  • 返回值的类型是 (void*) 型,所以malloc函数并不知道开辟空间的类型,需要使用者自己决定
  • 如果size=0,malloc的行为是未定义的,取决于编译器。
int* p = (int*)malloc(40);
free释放空间

C语言提供的动态开辟回收的函数:

函数声明:

void free( void *ptr);

free释放动态开辟的内存。

  • 如果参数ptr指向的空间不是动态开辟的(栈、静态),则free函数的行为是未知的。
  • 如果参数ptr是NULL,则free不执行任何操作。

1、free的参数只能是一块动态内存的起始地址。

2、没有free释放空间,并不是说内存空间就不回收了。
3、当程序退出时,系统会自动回收内存空间。


clloc初始化开辟空间

函数声明:

void *calloc( size_t num, size_t size );
/*
num:Number of elements
size:Length in bytes of each element
*/

calloc开辟内存空间的同时初始化。

  • 为num个大小为size的元素开辟一块空间,并把每块空间的每个字节初始化为0。
realloc修改动态内存

改变动态内存分配的空间。

函数声明:

void *realloc( void *ptr, size_t size );
//ptr:要修改的空间地址
//size:修改的空间大小
  • realloc函数会找一块新的空间,将旧空间的拷贝到新空间,再将旧的空间释放掉。
  • 返回新空间的地址。

realloc的应用:

int main()
{
	int* p = (int*)malloc(40);
	if (NULL == p)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i + 1;
	}
	//扩容
	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;
}

常见的内存错误

1、对空指针的解引用操作
void test()
{
	int* p = (int *)malloc(3);
	*p = 20;//如果p的指针是NULL,就会报错
	free(p);
}

 改正:在使用malloc返回的指针前,对malloc进行一次检查。

if (NULL == p)
{
	printf("%s\n", strerror(errno));
	return 1;
}
2、对动态开辟空间的越界访问
int main()
{
	int* p = (int*)malloc(40);
	if (NULL == p)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	int i = 0;
	for (i = 0; i <= 10; i++)
	{
		p[i] = i;//i=10,越界访问
	}
	free(p);
	p = NULL;
}
3、对非动态开辟内存使用free释放
int main()
{
	int a = 10;
	int* p = &a;
	free(p);
	p = NULL;
	return 0;
}
4、使用free释放动态内存的一部分 
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
	}
	//使用
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*p = i;
		p++;//初始化完成后,p不能找到动态内存的起始位置
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

 改正:把动态内存的起始地址存储起来

int* ptr = p;
free(ptr);
ptr = NULL;
5、对同一块空间的多次释放
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
	}
	free(p);
    free(p);
}

改正:在释放完成后,立刻把p设为空指针,free(NULL)不执行任何操作。

	free(p);
    p = NULL;
6、动态开辟内存忘记释放

忘记释放动态内存容易造成内存泄漏。

 内存泄漏:程序开辟的动态内存没有释放,开辟空间的起始地址又不能找到。

1、对该程序来说,这片内存既不能被利用,也不能被找到,对该程序而言,这块内存造成内存泄漏。

2、当程序终止运行时,操作系统会自动释放程序所占用的内存,这部分找不到的内存也会被释放掉。

void test()
{
	int* p = (int*)malloc(40);
	int flag = 0;
	scanf("%d", &flag);
	if (flag == 5);
	return;
	free(p);
	p = NULL;
}

动态内存的经典题目

1、请问调用Test函数会产生怎样的结果?

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

 对printf参数的补充:

    printf("helloworld\n");
    char* p = "helloworld\n";
    等价于
    printf(p);

2、这些程序的为问题的原因是?

int* f1(void)
{
	int x = 10;
	return (&x);
}//野指针

int* f1(void)
{
	int* ptr;
	*ptr = 10;
	return ptr;
}//野指针

 3、Test可以打印出来helloworld吗?

char* GetMemory()
{
	char p[] = "hello world";
	return p;
}

void Test()
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}

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

4、 Test可以打印出来helloworld吗?

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

        这里的Test可以打印出来,但是要记得释放掉开辟的内存空间。

4、 Test可以打印出来world吗?

Test()
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL);
	{
		strcpy(str, "world");
		printf(str);
	}
}

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

 ​

标签:malloc,str,int,void,free,C语言,动态内存,之四,NULL
From: https://blog.csdn.net/2202_75570363/article/details/143764168

相关文章

  • 【C语言】前端未来
    你对前端未来的技术趋势有何看法?例如WebAssembly、WebXR、PWA等。 对未来前端技术趋势的看法,我认为有几个关键方向正在快速发展: WebAssembly(WASM):随着性能需求的增长,WebAssembly作为一种低级字节码运行环境,使得开发者可以编写高性能的原生代码并在浏览器上运行,这将显著......
  • 【C语言】前端虚拟DOM
    你对前端框架中的虚拟DOM有何理解?它与真实DOM有何区别和联系?虚拟DOM(VirtualDOM)是一种由前端框架如React、Vue等创建的概念,用于优化网页渲染过程。它是实际DOM的一个轻量级副本,存储在内存中,由JavaScript动态构建。每当组件的状态改变时,虚拟DOM会计算出新的状态与之前的差异,而......
  • 【c语言】浏览器的渲染机制。
    请详细解释浏览器的渲染机制,特别是关于重绘和重排的过程。 浏览器的渲染机制是一个复杂的过程,它涉及到HTML文档的解析、CSS样式计算、布局以及最终呈现到屏幕上的过程。主要包括两个关键的概念:重绘(Repainting)和重排(Reflow,也称为Layout)。 1.变更检测(ChangeDetection):当......
  • C语言在linux上实现进程线程开发
    环境操作系统:Linux线程创建线程创建函数#include<pthread.h>/**@description线程创建函数*@paramtidp线程标识符*@paramattr线程属性指针*@paramstart_rtn线程执行函数(void*fun(void*))*@paramarg线......
  • C语言习题~day16
    1.关于函数调用说法不正确的是:()A.函数可以传值调用,传值调用的时候形参是实参的一份临时拷贝B.函数可以传址调用,传址调用的时候,可以通过形参操作实参C.函数可以嵌套定义,但是不能嵌套调用D.函数可以嵌套调用,但是不能嵌套定义答案解析:A:正确,形参按照值的方式传递,将来形参就......
  • C语言期末必练题目——part 12(编程题)
    10.有一个分数序列:2/1,3/2,5/3,8/5,13/8,……编程求这个序列的前20项之和。#include<stdio.h>voidmain(){ inti,t,n=20; floata=2,b=1,s=0;for(i=1;i<=n;i++){s=s+a/b;t=a;a=a+b;b=t;} printf("sum=%6.2f",s);}11.从键盘输入两个数,求出其最大值(要求使用函数完......
  • 初识C语言|素数代码之——你的代码,我的代码,好像不一样
        嘿,大家好!咱作为大一新生,来聊聊C语言输出素数这事儿。其实,说白了,这就像玩游戏找宝藏一样,路数有好多呢。咱可以老老实实用简单办法,一个个试。不过这有点傻,像个愣头青。还有个神奇的筛法,就像用个大网把合数都捞走,剩下的就是宝贝素数啦。感觉C语言像个大迷宫,找素数......
  • 关于我重生到21世纪学C语言这件事——指针详解(3)
    人无完人,持之以恒,方能见真我!!!共同进步!!文章目录1.字符指针变量2.数组指针变量3.⼆维数组传参的本质4.函数指针变量5.函数指针数组6.转移表1.字符指针变量在指针的类型中我们知道有⼀种指针类型为字符指针char*;⼀般使⽤:intmain(){charch='w......
  • c语言程序
    1.第⼀个C语⾔程序#include<stdio.h>intmain(){printf("helloC\n");return0;}​2.main函数每个C语⾔程序不管有多少⾏代码,都是从main函数开始执⾏的,main函数是程序的⼊⼝,main函数也被叫做:主函数。main前⾯的int表⽰main函数执......
  • C语言的常见概念------1
    1.课前准备1.1什么是C语言C语言是一门计算机语言,而且是编译型的计算机语言。所以C 语言源代码都是文本文件,而计算机只能够识别二进制指令,所以我们需要对文件进行编译和链接,把文本文件翻译成二进制指令之后,机器才能执行如图,1.2选择集成开发环境集成开发环境(IDE):集编译器,......