第五十四天学习记录:C语言进阶:动态内存管理Ⅱ

这篇具有很好参考价值的文章主要介绍了第五十四天学习记录:C语言进阶:动态内存管理Ⅱ。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

常见的动态内存错误

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

int* p=(int*)malloc(4);
//p进行相关的判断
*p=10;//malloc开辟空间失败,有可能对NULL指针解引用
free(p);
p=NULL;

2、对动态开辟的内存的越界访问

int* p=(int*)malloc(40);//10个int
if(p!=NULL)
{
	int i=0;
	//越界
	for(i=0;i<=10;i++)
	{
		*(p+i)=i;
	}
	free(p);
	p=NULL;
}

3、对非动态开辟内存的free

int a=10;
int* p=&a;
//...
//对非动态开辟内存的free
free(p);

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

int* p=(int*)malloc(40);
if(p!=NULL)
{
	int i=0;
	for(i=0;i<10;i++)
	{
		*p++ = i;
	}
	free(p);
	p=NULL;
}

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

int* p=(int*)malloc(40);
if(p!=NULL)
{
	//使用
	free(p);
	//...
	free(p);
}

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

#define _CRT_SECURE_NO_WARNINGS 1

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

int main()
{
	int i = 0;
	while (1)
	{
		++i;
		printf("%d\n", i);
		malloc(1);
	}
	return 0;//死机警告!!!不要轻易尝试
}

问:realloc的第一个参数的指针地址必须是malloc或calloc创建的在堆上的地址吗?
ChatAI答:
是的,realloc 的第一个参数必须是由 malloccallocrealloc 函数返回的指针,且该指针必须指向堆上申请的内存空间。如果该指针不合法,也就是不是由上述函数返回的指针,或者指向的不是堆上的内存空间,那么 realloc 可能会导致未定义的行为。

问:realloc的第一个参数的指针也可以为NULL
ChatAI答:
是的,realloc 函数的第一个参数也可以是 NULL 指针。如果第一个参数是 NULL,那么 realloc 的行为和 malloc 的行为是等价的,如果分配内存成功,realloc 将会返回一个指向新内存块的指针。

例如下面的代码可以在堆上分配一段初始化为 0 的内存空间:

int *p = NULL;
p = realloc(p, sizeof(int) * 10);
if (p == NULL) {
    // 分配内存失败的处理
    printf("realloc failed");
} else {
    // 分配内存成功的处理,继续使用 p 指向的内存空间
    printf("realloc succeeded");
}

在函数调用后,如果 realloc 成功了,那么 p 将会指向一个大小为 10 个整数的内存块,并且内存块中的每个整数都将会被初始化为 0。如果 realloc 调用失败了,那么 p 会继续指向 NULL,程序会打印 “realloc failed”。

问:多次free(NULL);也不会报错吗?
ChatAI答:
在 C 语言中,多次调用free(NULL)不会导致程序崩溃或错误。因为在 C 标准中,对 NULL 指针调用 free 函数是被允许的,且不会影响程序的执行。

值得注意的是,在多次释放同一个内存指针时,可能会导致不可预测的错误,这应该避免这种情况的发生,通常情况下在free 之后将指针置空,避免出现重复释放的问题。

例题1:
请问以下代码会有什么样的结果?

#define _CRT_SECURE_NO_WARNINGS 1

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

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}

void Test()
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}

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

1、运行代码程序会出现崩溃的现象
2、程序存在内存泄漏的问题
str以值传递的形式给p
p是GetMemory函数的形参,只能函数内部有效
等GetMemory函数返回之后,动态开辟内存尚未释放
并且无法找到,所以会造成内存泄漏

改进1:

#define _CRT_SECURE_NO_WARNINGS 1

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

void GetMemory(char** p)
{
	*p = (char*)malloc(100);
}

void Test()
{
	char* str = NULL;
	GetMemory(&str);
	assert(str);
	strcpy(str, "hello world");
	printf(str);
	free(str);
	str = NULL;
}

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

改进2:

#define _CRT_SECURE_NO_WARNINGS 1

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

char* GetMemory(char* p)
{
	p = (char*)malloc(100);
	return p;
}

void Test()
{
	char* str = NULL;
	str=GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
	free(str);
	str = NULL;
}

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

例题2:
请问以下代码会有什么样的结果?

#define _CRT_SECURE_NO_WARNINGS 1

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

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

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

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

非法访问。
p的内容已被回收,但地址返回了。

例题3:

#define _CRT_SECURE_NO_WARNINGS 1

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

void GetMemory(char **p,int num)
{
	*p = (char*)malloc(num);
}

void Test()
{
	char* str = NULL;
	GetMemory(&str,100);
	strcpy(str, "hello");
	printf(str);
}

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

内存泄漏。

例题4:

#define _CRT_SECURE_NO_WARNINGS 1

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

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

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

非法访问。
篡改动态内存区的内容,后果难以预料,非常危险。
因为free(str);之后,str成为野指针。if (str != NULL)不起作用。

第五十四天学习记录:C语言进阶:动态内存管理Ⅱ
图片转自比特科技

C/C++程序内存分配的几个区域:
1、栈区(stack):在执行函数时,函数内局部变量的储存单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
2、堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配方式类似于链表。
3、数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
4、代码段:存放函数体(类成员函数和全局函数)的二进制代码。

实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量除了作用域就销毁。
但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁,所以生命周期变长。文章来源地址https://www.toymoban.com/news/detail-460967.html

到了这里,关于第五十四天学习记录:C语言进阶:动态内存管理Ⅱ的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 学习c#的第二十四天

    目录 C# 事件(Event) 事件概述 如何订阅和取消订阅事件 以编程方式订阅事件 使用匿名函数订阅事件 取消订阅 如何发布符合 .NET 准则的事件 发布基于 EventHandler 模式的事件 如何在派生类中引发基类事件 如何实现接口事件 如何实现自定义事件访问器 示例 事件(Event) 基本

    2024年02月04日
    浏览(40)
  • Python学习笔记第六十四天(Matplotlib 网格线)

    我们可以使用 pyplot 中的 grid() 方法来设置图表中的网格线。 grid() 方法语法格式如下: 参数说明: b:可选,默认为 None,可以设置布尔值,true 为显示网格线,false 为不显示,如果设置 **kwargs 参数,则值为 true。 which:可选,可选值有 ‘major’、‘minor’ 和 ‘both’,默认为

    2024年02月12日
    浏览(44)
  • 15天学习MySQL计划(运维篇)分库分表-监控-第十四天

    1.介绍 1.问题分析 ​ 随着互联网及移动互联网的发展,应用系统的数据量也是成指数式增加,若采用但数据进行数据存储,存在以下性能瓶颈: IO瓶颈:热点数据太多,数据库缓存不足,产生大量磁盘IO,效率较低。请求数据太多,带宽不够,网络IO瓶颈。 CPU瓶颈:排序,分

    2024年02月05日
    浏览(49)
  • 第五十三天学习记录:C语言进阶:动态内存管理Ⅰ

    问: 栈区堆区静态区的大小是固定的吗?如果栈区满了,会向后2者借位置吗? ChatAI答: 栈区、堆区和静态区的大小通常是由操作系统或编译器预定义的,不是固定的。这些区域的大小通常受到多种因素的影响,如系统物理内存大小、进程虚拟地址空间的大小、编译器和操作

    2024年02月06日
    浏览(66)
  • 第四十四天

    ●scoped 属性      HTML5 中的新属性(布尔属性)。如果使用该属性,则样式仅仅应用到 style 元素的父元素及其子元素。父组件的样式将不会渗透到子组件中。      实现组件的私有化,不对全局造成样式污染,表示当前style属性只属于当前模块。 ●CSS动画      动画允许元素

    2024年02月16日
    浏览(46)
  • 第四十四天打卡

    零钱兑换 II 中等 1K 相关企业 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带符号

    2024年02月03日
    浏览(49)
  • 【力扣刷题 | 第二十四天】

    目录 前言: 1049. 最后一块石头的重量 II - 力扣(LeetCode) 494. 目标和 - 力扣(LeetCode) 总结:                  今天我们依然暴打动态规划 有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合,从中选出任意两块石头,然后将它们一起粉

    2024年02月14日
    浏览(38)
  • 算法练习第六十四天

    LCR 184. 设计自助结算系统 - 力扣(LeetCode) 总结:利用一个双端维护队列一个往后递减的队列,对头是最大值,每次进入一个新值时就一直和队尾元素比较将比新的值小的数排出,这样能保证留在队列中的数都是会对最大值产生影响的数,而当主队列中将要排出的数与双端队

    2024年02月07日
    浏览(58)
  • MFC补充第十四天 句柄嫁接与子类化

    句柄嫁接与子类化: a)Attach和Detach就是单纯的嫁接与分离函数。 对象一旦嫁接入一个句柄,就可以自由地调用CWnd或其派生类的功能。 b)子类化Subclass内部包含Attach,额外再增加一个消息转拨到派生类(SubClass就是子类) c)SubClassWindow函数内部核心功能就是Attach和::SetWindowLong

    2024年02月16日
    浏览(48)
  • 十四天学会C++之第五天:类的详细讨论

    什么是友元函数和友元类,它们的作用。 如何声明和使用友元函数和友元类,访问类的私有成员。 友元函数(Friend Functions) 友元函数是一种特殊的函数,它被允许访问类的私有成员。这意味着即使成员是私有的,友元函数也能够直接访问它们,而不需要通过公有接口。这提

    2024年02月07日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包