【动态内存错误详解和C的内存分区】

在这里插入图片描述

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤
📃个人主页 阿然成长日记 👈点击可跳转
📆 个人专栏: 🔹数据结构与算法🔹C语言进阶
🚩 不能则学,不知则问,耻于问人,决无长进
🍭 🍯 🍎 🍏 🍊 🍋 🍒 🍇 🍉 🍓 🍑 🍈 🍌 🍐 🍍

💻1.动态内存错误

(1)对NULL指针的解引用操作
(2)对动态开辟空间的越界访问
(3)对非动态开辟内存使用free释放
(4)使用free释放一块动态开辟内存的一部分
(5)对同一块动态内存多次释放
(6)动态开辟内存忘记释放(内存泄漏)

📝2.经典案例分析

2.1案例一

#include<stdio.h>
#include<string.h>
void getmemory(char* p)
{
	p = (char*)malloc(100);
}
void test(void)
{
	char* str = NULL;
	getmemory(str);
	strcpy(str, "hello");
	printf("%s", str);
}
int main()
{
	test();
	return 0;
}

2.1.1问题分析

上述代码运行失败。
在这里插入图片描述

首先回顾一下知识;

值传递:将实参的值复制到形参相应的存储单元中,即形参和实参分别占用不同的存储单元。
地址传递:形参并不存在存储空间,编译系统不为形参数组分配内存。因此在数组名或指针作函数参数时所进行的传送只是地址传送,形参在取得该地址之后,与实参共同拥有一段内存空间,形参的变化也就是实参的变化。

原因是:1.因为这里是值传递,p和str分别占用不同的存储单元,malloc只是给p开辟了空间,而str仍然指向空指针。所以strcpy赋值时失败。(对NULL指针的解引用操作
2.没有free(p);造成内存泄漏。(动态开辟内存忘记释放)
注意:printf(“%s”, str);与printf(str);是一样的。

2.1.2修改错误

#include<stdio.h>
#include<string.h>
void getmemory(char** p)
{
	*p = (char*)malloc(100);
}
void test(void)
{
	char* str = NULL;
	getmemory(&str);
	strcpy(str, "hello");
	printf("%s", str);
	free(str);
	str = NULL;
}
int main()
{
	test();
	return 0;
}

在这里插入图片描述
运行成功

  1. 修改为地址传参
    下图是一个指向关系图:
    由图可见:str是一级指针,p是二级指针
    在这里插入图片描述
    2.增加了free释放
free(str);
	str = NULL;

📝2.2案例二

#include<stdio.h>
#include<string.h>
char* getmemory(void)
{
	char p[] = "hello word";
	return p;
}
void test(void)
{
	char* str = NULL;
	str = getmemory();
	printf("%s", str);
	free(str);
	str = NULL;
}
int main()
{
	test();
	return 0;
}

2.2.1 原因分析

原因就出在下面代码上;

char* getmemory(void)
{
	char p[] = "hello word";
	return p;
}

数组p临时申请的那块空间,在退出这个函数的时候,就被释放掉了。
虽然我们的str可以获得p的地址,但是p指向的东西未知,p就相当于野指针。访问时,就会造成非法访问
属于:返回栈空间地址问题。

2.2.2 解决问题

#include<stdio.h>
#include<string.h>
char* getmemory(void)
{
	static char p[] = "hello word";
	return p;
}
void test(void)
{
	char* str = NULL;
	str = getmemory();
	printf("%s", str);
	free(str);
	str = NULL;
}
int main()
{
	test();
	return 0;
}

在这里插入图片描述
运行成功
我只是增加了static静态修饰符。

static char p[] = "hello word";

为什么这样就可以呢?首先我们来回顾static的作用

实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁,
所以生命周期变长。

所以,我们使用static修饰过后,当退出这个函数时,这个变量就会一直保存,我们再次调用时,仍是保存上一次的调用结果。此时,str可以获得p的地址。并且,p不再是野指针。

⚠️c/c++内存分布

在这里插入图片描述
内核空间,内存映射段。

1.2 内存分区简介

1.2.1 栈区(stack)

栈区编译器自动分配释放,由操作系统自动管理,无须手动管理。
栈区上的内容只在函数范围内存在,当函数运行结束,这些内容也会自动被销毁。


1.栈区按内存地址由高到低方向生长,其最大大小由编译时确定,速度快,但自由性差,最大空间不大。
2.栈区是先进后出原则,即先进去的被堵在屋里的最里面,后进去的在门口,释放的时候门口的先出去。
栈区存放内容


临时创建的局部变量和const定义的局部变量存放在栈区。
函数调用和返回时,其入口参数和返回值存放在栈区。

1.2.2 堆区(heap)

堆区由程序员分配内存和释放。

堆区按内存地址由低到高方向生长,其大小由系统内存/虚拟内存上限决定,速度较慢,但自由性大,可用空间大。

堆区动态申请与释放内存用malloc(),free()等函数实现动态分布内存。

1.2.3 全局(静态)区

通常是用于那些在编译期间就能确定存储大小的变量的存储区,但它用于的是在整个程序运行期间都可见的全局变量和静态变量。

全局区有 .bss段 和 .data段组成,可读可写

1 bss段
未初始化的全局变量和未初始化的静态变量存放在.bss段。
初始化为0的全局变量和初始化为0的静态变量存放在.bss段。
.bss段不占用可执行文件空间,其内容由操作系统初始化。
2data段
已初始化的全局变量存放在.data段。
已初始化的静态变量存放在.data段。
.data段占用可执行文件空间,其内容有程序初始化。

1.2.4 常量区

字符串、数字等常量存放在常量区。 const修饰的全局变量存放在常量区。 程序运行期间,常量区的内容不可以被修改。

1.2.5 代码区

程序执行代码存放在代码区,其值不能修改(若修改则会出现错误)。 字符串常量和define定义的常量也有可能存放在代码区。

各位看官老爷,咱下回再见!
别忘了点赞关注加评论哟
💙 💜 ❤️ 💚 💔 💓 💗 💕 💞 💘 💖 ✨ ⭐️ 🌟

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿然成长日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值