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

常见的动态内存错误

  • 1.动态内存错误
  • 2.经典案例分析
    • 2.1案例一
      • 2.1.1**问题分析**
      • 2.1.2**修改错误**
    • 2.2案例二
      • 2.2.1 原因分析
      • 2.2.2 解决问题
  • c/c++内存分布
    • 1.2 内存分区简介
      • 1.2.1 栈区(stack)
      • 1.2.2 堆区(heap)
      • 1.2.3 全局(静态)区
      • 1.2.4 常量区
      • 1.2.5 代码区

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定义的常量也有可能存放在代码区。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/28492.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Argo CD 入门扫盲使用

目录 一、什么是 argo cd 二、为什么使用 argo cd 三、argo cd 架构图 四、Argo CD 使用 1、安装 Argo CD 2、安装 Argo CD CLI 3、发布 Argo CD 服务 4、获取 Argo CD 密码 5、准备 Git 仓库 6、创建 Argo CD App 7、版本升级 8、版本回滚 一、什么是 argo cd A…

STM32学习笔记(十三)丨USART通用同步/异步收发器(串口外设的基本使用丨串口发送数据、串口发送+接收数据)

本篇文章包含的内容 一、STM32的USART外设1.1 STM32的USAER外设简介1.2 USART外设的结构和工作原理1.3 串口通信数据帧1.4 起始位侦测和USART的噪声判断机制1.5 波特率发生器 二、串口发送和接收数据包2.1 HEX数据包2.2 文本数据包2.3 固定包长HEX数据包接收2.4 可变包长文本数…

简单认识MySQL数据库日志和数据的备份恢复

文章目录 Mysql 备份与还原一、数据备份的重要性二、数据库备份类型1 、物理备份2 、逻辑备份 三、常见的备份方法1、 物理冷备2、 专用备份工具 mysqldump 或 mysqlhotcopy3、 启用二进制日志进行增量备份3.4 第三方工具备份 四、MySQL完全备份1、简介2、优点&#xff1a;3、缺…

SAP ABAP 报表程序实现下载文件及上传 Excel 并解析

步骤1&#xff1a; 事务代码 SMW0 选择二进制数据选项点击上方按钮。 点击新建按钮输入名称和描述&#xff0c;上传模版文件。 案例传入 EXCEL 如下&#xff1a; 创建好资源库对象结果如下。 步骤2&#xff1a;报表效果展示 点击按钮选择上传的文件。 解析 Excel 文件结果…

visual studio配置调用c++ dll opencv为例

1&#xff0c;配置VC目录&#xff0c;包含目录和库目录。 2&#xff0c;链接器->输入->包含目录 3&#xff0c;生成目录下包含对应的dll文件 4&#xff0c;需注意对应的Debug&#xff0c;Release及X86&#xff0c;X64选项

创建型模式 - 建造者模式

概述 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 分离了部件的构造(由Builder来负责)和装配(由Director负责)。 从而可以构造出复杂的对象。这个模式适用于&#xff1a;某个对象的构建过程复杂的情况。 由于实现了构建和装配的解耦。…

Java基础---常用类大全以及各数据结构的方法大全

目录 前言 一、Math类 二.Scanner类 三、String类、StringBuilder和StringBuffer类 &#x1f496;String类 &#x1f496;StringBuilder和StringBuffer 四.Arrays类 五.Random类 六.时间类 七.ArrayList顺序表 八、LinkedList与链表 九.Stack栈和Queue队列 十.Pri…

SpringCloud Alibaba——Ribbon的属性配置和类配置优先级

目录 一、Ribbon的属性配置和类配置哪个优先级高二、Ribbon的属性配置和类配置优先级源码解读 一、Ribbon的属性配置和类配置哪个优先级高 类配置优先级高 二、Ribbon的属性配置和类配置优先级源码解读 通过RibbonClientConfiguration类中的ribbonRule方法可知&#xff0c;优…

guava限流器RateLimiter使用简介(Springboot实现)

在大型分布式系统中&#xff0c;限流是一种重要的防护机制&#xff0c;可以帮助我们控制流量并减轻系统的负担。Google的Guava库提供了一种方便的限流器实现&#xff0c;可以帮助我们轻松地实现限流功能。本文将介绍Guava中限流器的基本概念和使用方法。 一、什么是限流器&…

实现小程序商城首页【源码公开】

效果图 页面源码 <view class"index-container"><view class"header"><!--搜索框【仅样式&#xff0c;不做处理】 start--><van-search bindtap"clickSearch" disabled shape"round" background"#9c7bf0&q…

GPT与人类:人工智能是否能够真正复制人类语言?

人类语言是一种复杂的系统&#xff0c;它不仅包含着无数单词和语法规则&#xff0c;更重要的是具有丰富的含义和上下文。这些语言特征涉及到常识、文化、情感和经验等方面&#xff0c;是人类在长期进化和文明发展中所积累起来的丰富知识和经验的体现。然而&#xff0c;人工智能…

Java对日志文件进行加密

最近碰到了一个新的需求&#xff0c;生产环境中Java程序部署的服务器会定期清理数据&#xff0c;需要将保存在程序所在服务器上的日志文件挂载到网盘上&#xff0c;但又不想让用户看到日志文件中的信息&#xff0c;因此需要对日志文件中的内容进行加密。 这里&#xff0c;并不是…