数据结构(Day13)

一、学习内容

  1. 内存空间划分

    1. 1、一个进程启动后,计算机会给该进程分配4G的虚拟内存
      2、其中0G-3G是用户空间【程序员写代码操作部分】【应用层】
      3、3G-4G是内核空间【与底层驱动有关】
      4、所有进程共享3G-4G的内核空间,每个进程独立拥有0G-3G的用户空间
      5、内存分区的目的是:专人专项、提高效率

      1. 栈区特点

        1. 运行时自动分配和回收: 栈是自动管理的,程序员不需要手工干预,使用起来方便简单。

        2. 反复使用: 栈内存在程序中其实就是那一块空间,程序反复使用这一块空间。

        3. 脏内存: 栈内存由于反复使用,每次使用后程序不会去清理,因此分配到时如果没有初始化会保留原来的值。

        4. 临时性: 函数不能返回栈变量的指针,因为这个空间是临时的。

        5. 栈会溢出: 因为操作系统事先给定了栈的大小,如果在函数中无穷尽的分配栈内存总会消耗完。

        6. 栈空间是向下增长的

      2. 堆区特点

        1. 大块内存: 堆内存管理是总量很大的操作系统内存块,各进程可以按需申请使用,使用完释放。

        2. 程序手动申请和释放: 手工意思是需要写代码去申请malloc()和释放free()。

        3. 脏内存: 堆内存也是反复使用的,而且使用者用完释放前不会清除,因此也是脏的。

        4. 临时性: 堆内存只在申请malloc()和释放free()之间属于这个进程,可以访问。在释放free()之后不能再访问,否则会有不可预料的后果。

        5. 不可直接操作: 需要通过指针操作。

        6. 堆空间是向上增长的

      3. 堆和栈的区别

        1. 管理分配效率不同。栈编译器自动管理,无需程序员手工控制;而堆空间的申请释放工作由程序员控制,容易产生内存泄漏。堆的效率比栈要低

        2. 空间大小不同。栈是向低地址扩展的数据结构,是一块连续的内存区域。堆是向高地址扩展的数据结构,是不连续的内存区域。

        3. 是否产生碎片。对于堆来讲,频繁的malloc/free(new/delete)势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低(虽然程序在退出后操作系统会对内存进行回收管理)。对于栈来讲,则不会存在这个问题。

        4. 增长方向不同。堆的增长方向是向上的,即向着内存地址增加的方向;栈的增长方向是向下的,即向着内存地址减小的方向。

  2. 动态内存分配和回收(申请和释放)

    • malloc函数

      • 功能:在堆区开辟指定字节的空间

      • 头文件:#include <stdlib.h>

      • 原型:void *malloc(size_t size);

      • 分析:返回值是void *【通用型指针】——可以转换成任意类型的指针

        • 参数是size_t size 【数据类型 size_t 64位是long int——%ld 32位是int——%d】 【size表示的是索要开辟的空间的大小】

    • free函数

      • 功能:手动释放堆区空间

      • 头文件:#include <stdlib.h>

      • 原型:void free(void *ptr); //无返回值函数 参数是void *ptr 【通用型指针,传参直接传指针名即可】

    • 常见的内存错误

      • 野指针
        • 原因

          • 指向动态分配内存的指针在释放后,没有NULL

          • 指向被删除的对象或无效对象的指针

          • 接收了函数返回的局部变量的指针

        • 解决方法

          • malloc() free()后及时NULL

          • 及时更新指针

          • 编码时明确变量的作用域,不要接收返回的局部变量的指针

      • 内存越界
        • 原因

          • 访问到野指针指向的区域,越界访问

          • 数组下标越界访问

          • 向缓冲区写入超过其容量的数据【strcpy、strcat的例子】

        • 解决方法

          • 使用数组时确保下标的范围,使用循环计数或条件判断控制范围

          • 使用指针时,进行if(NULL==p)的检查

          • 使用strncat 和 strncpy可以确保不会溢出目标缓冲区 strcpy(dest , src); strncpy(dest , src , strlen(dest))

      • 内存泄漏
        • 如果没有适时释放被动态分配的内存,会导致内存泄露问题。未释放的内存一直占用系统资源,使得系统变慢并最终导致崩溃。

          • 原因

            • 丢失了分配的内存的首地址,导致无法释放

            • 每循环一次,泄露一次内存

            • 非法访问常量区

          • 解决方法

            • 及时使用free()释放不再使用的内存

            • 合理设计数据结构和算法,避免内存无线增长

            • 设置合理的变量【作用域】,减少内存占用

  3. 脑图

二、作业

  1. p 和 "hello,world"存储在内存哪个区域?( ) (鲁科安全)

int main()

{

    char *p = "hello,world";

    return 0;

}

解析:

字符串 "hello,world" 是一个字符串字面量,在编译时会存储在程序的 常量区(或者叫做只读数据段) 中。该区域通常是只读的,不能被修改。
变量 p 是一个指针变量,它指向字符串 "hello,world"。因为 p 是在 main() 函数中定义的局部变量,所以它存储在 栈区 中。

解答:

*p在栈区  "hello,world"在静态区中的.ro段

  1. 一个由C/C++编译的程序,会将占用的内存分为几个部分:堆、栈、代码段、数据段、BSS段。请问以下程序中的变量a、b、c、d,分别被存在内存的哪个部分?(泰华智慧)

int a = 0;

char *b;

int main()

{

    int c;

    static char d = 'a';

    b = malloc(10);

    *b = d;

    return 0;

}

解析:

变量 a

变量 a 是一个全局变量并且初始化为 0

存储区域:数据段(已初始化的全局变量存储在数据段)。

变量 b

变量 b 是一个全局指针变量,但它没有初始化。

存储区域:BSS 段(未初始化的全局变量和静态变量存储在 BSS 段中)。

变量 c

变量 c 是在 main 函数内部定义的局部变量。

存储区域:栈区(局部变量存储在栈区)。

变量 d

变量 d 是一个静态局部变量并初始化为 'a'

存储区域:数据段(静态局部变量也存储在数据段中,因为它已初始化)。

解答:

变量a在数据段、b在bss段、c在栈区、d在数据段

 

  1. 如下代码:变量g_iA,g_iB,g_iC,iD,iE, iF, piG,iH 分别在内存中的什么区( ) (H3C)

int g_iA = 1;

int g_iB;

static int g_iC = 1;

void func1(){

    static int iD=2;

    iD++;

    int iE=2;

    iE++;

}

void func2(){

    int iF=3;

    int *piG = (int*) malloc(4);

}

int main(){

    int iH = 100;

}

解析:

g_iA

这是一个已初始化的全局变量。

存储区域:data段(已初始化的全局变量存储在数据段中)。

g_iB

这是一个未初始化的全局变量。

存储区域:BSS 段(未初始化的全局变量存储在 BSS 段中)。

g_iC

这是一个已初始化的静态全局变量。

存储区域:data段(已初始化的静态全局变量也存储在数据段中)。

iD

这是在 func1 函数中定义的已初始化的静态局部变量。

存储区域:data段(静态局部变量存储在数据段中,无论函数是否被调用)。

iE

这是在 func1 函数中定义的局部变量。

存储区域:栈区(局部变量存储在栈中,每次函数调用时都会分配新的内存)。

iF

这是在 func2 函数中定义的局部变量。

存储区域:栈区(局部变量存储在栈中)。

piG

这是在 func2 函数中定义的指针,malloc(4) 动态分配了 4 字节的内存,piG 指向这块内存。

存储区域:栈区(指针 piG 本身) 和 堆区(malloc(4) 动态分配的内存)。

iH

这是在 main 函数中定义的局部变量。

存储区域:栈区(局部变量存储在栈中)

解答:

g_iA在静态区中的.data段、g_iB在静态区的.bss段、g_iC在静态区的.data段、iD在静态区的.data段、iE在栈区、iF在栈区、piG在栈区、iH在栈区

 

  1. 有关内存的思考题 (山东山大电力技术有限公司,登虹科技,昆腾微电子)

void GetMemory(char *p)

{

           p =(char *)malloc(100);

}

void Test(void)

{

      char *str=NULL;

    GetMemory(str);

    strcpy(str,"hello world");

    printf(str);

}

请问运行 Test 函数会有什么样的结果?

char * GetMemory(void)

{

           char pl[] = "hello world"; //char *p = "hello world"

     return p1;

}

Void Test(void)

{

           char *str=NULL;

     str = GetMemory();

     printf(str);

}

请问运行 Test 函数会有什么样的结果?

void GetMemory(char **p,int num)

{

           *p = (char *)malloc(num);

}

void Test(void)

{

    char *str = NULL;

    GetMemory(&str, 100);

    strcpy(str, "hello world");

    printf(str);

}

请问运行 Test 函数会有什么样的结果?

void Test (void)

{

      char *str = (char *)malloc(100);

    strcpy(str,"hello");

    free(str);

    if(str != NULL)

    {

        strcpy(str, "world");

        printf(str);

    }

}

请问运行 Test 函数会有什么样的结果?

解析:

1、第一段

GetMemory 函数中,参数 p 是一个局部指针变量,当 malloc(100) 动态分配内存时,只是将地址赋值给局部的 p,而不会影响调用者 Test 中的 str。换句话说,str 仍然是 NULL。随后在 Test 中调用 strcpy(str, "hello world") 时,会试图向 NULL 指针中写入数据,这会导致 段错误(Segmentation Fault)。

2、第二段
GetMemory 函数中,p1[] 是一个局部数组,存储在栈区。当 GetMemory 函数返回时,栈帧将被释放,局部变量 p1 将变为无效。虽然 GetMemory 返回 p1 的地址,但是这个地址指向已经被释放的栈空间,尝试访问该地址的内容是未定义行为。

3、第三段
GetMemory 中,传递的是 str 的地址(&str),因此函数可以正确地分配内存并将其赋值给 str。随后 strcpy(str, "hello world") 将字符串拷贝到分配的内存中,并调用 printf(str) 输出字符串。
4、第四段
malloc(100) 分配了 100 字节的内存,随后 strcpy(str, "hello") 将字符串 "hello" 拷贝到这块内存。free(str) 释放了 str 指向的内存。虽然在 C 中,调用 free 并不会自动将指针置为 NULL,但在 if (str != NULL) 语句中,str 依然保持其原来的值(它指向已释放的内存)。由于内存已经被释放,再次尝试 strcpy(str, "world") 将会向一块已经释放的内存写入数据,这是未定义行为。

解答:

第一段:段错误

第二段:段错误

第三段:"hello,world"

第四段:程序崩溃

  1. 堆和栈的区别是什么? (矩阵软件)

解答:

  1. 管理分配效率不同。栈编译器自动管理,无需程序员手工控制;而堆空间的申请释放工作由程序员控制,容易产生内存泄漏。堆的效率比栈要低

  2. 空间大小不同。栈是向低地址扩展的数据结构,是一块连续的内存区域。堆是向高地址扩展的数据结构,是不连续的内存区域。

  3. 是否产生碎片。对于堆来讲,频繁的malloc/free(new/delete)势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低(虽然程序在退出后操作系统会对内存进行回收管理)。对于栈来讲,则不会存在这个问题。

  4. 增长方向不同。堆的增长方向是向上的,即向着内存地址增加的方向;栈的增长方向是向下的,即向着内存地址减小的方向。

  1. 什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法?(山大华天,智洋创新)

解答:

1、如果没有适时释放被动态分配的内存,会导致内存泄露问题。未释放的内存一直占用系统资源,使得系统变慢并最终导致崩溃。
2、

  1. 及时使用free()释放不再使用的内存

  2. 合理设计数据结构和算法,避免内存无线增长

  3. 设置合理的变量【作用域】,减少内存占用

三、总结

1. 学习内容概述

内存管理

通过对堆和栈的详细讨论,讲解了内存分配的过程。栈用于自动变量分配,堆用于动态内存分配.

动态内存分配与回收

使用 `malloc`、`free` 等函数来分配和释放内存。

重点包括 `malloc()` 函数的使用方法、返回指针的类型、以及如何合理使用 `free()` 函数来释放动态内存,避免内存泄漏。

内存优化

结合了动态分配的方式和内存碎片问题,指出合理的内存使用和优化内存分配策略的重要性。

高性能与低性能

介绍了高效的内存使用方法以及可能导致低效的内存管理方式,提醒避免内存碎片和滥用动态内存分配。

 2. 学习难点

指针的正确使用

在 `malloc()` 函数中分配内存时,返回的指针类型需要转换为适当的类型。初学者容易犯错的地方在于未进行正确的类型转换,或者忘记 `free()` 导致内存泄漏。

内存泄漏

在动态内存分配中,忘记释放不再使用的内存会导致内存泄漏问题,尤其是在大型程序中,内存泄漏将对程序性能产生严重影响。

内存碎片化

不合理的内存分配会导致内存的碎片化问题,影响程序的执行效率,理解并优化内存分配策略具有一定难度。

 3. 主要事项

内存分配时需要注意内存对齐

在某些平台上,内存分配需要根据特定的对齐要求进行,否则可能导致性能问题或程序崩溃。

动态内存分配的错误处理

如果 `malloc()` 返回 `NULL`,应及时处理以避免空指针引用。

合理使用 `free()` 释放内存

避免在释放后继续使用已释放的内存,使用悬空指针可能会导致未定义行为。

优化程序的内存使用

避免频繁的内存分配和释放操作,减少内存碎片问题。

 4. 未来学习的重点

深度理解内存分配的机制

更深入地理解堆栈的使用区别,尤其在不同平台上的表现差异。

内存调试工具的使用

掌握调试工具(如 `valgrind`)用于检测内存泄漏和内存管理错误,帮助排查问题。

进一步研究复杂数据结构中的内存管理

学习如何在链表、树、图等复杂数据结构中进行有效的动态内存分配和释放。

多线程程序中的内存管理

在并发编程中,了解内存管理的挑战,尤其是当多个线程共享内存时的同步问题。

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

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

相关文章

[数据集][目标检测]烟叶病害检测数据集VOC+YOLO格式612张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;612 标注数量(xml文件个数)&#xff1a;612 标注数量(txt文件个数)&#xff1a;612 标注类别…

【鸿蒙】HarmonyOS NEXT星河入门到实战7-ArkTS语法进阶

目录 1、Class类 1.1 Class类 实例属性 1.2 Class类 构造函数 1.3 Class类 定义方法 1.4 静态属性 和 静态方法 1.5 继承 extends 和 super 关键字 1.6 instanceof 检测是否实例 1.7.修饰符(readonly、private、protected 、public) 1.7.1 readonly 1.7.2 Private …

汽车免拆诊断案例 | 沃尔沃V40 1.9TD断续工作

故障现象 一辆04款的沃尔沃V40 1.9 TD&#xff0c;发动机代码D4192T3&#xff0c;使用博世EDC15C发动机管理。客户说车子断续工作&#xff0c;怀疑是正时皮带出现问题。卸下上皮带盖&#xff0c;检查发现皮带仍然在原来的位置上并且没有出现松动。起动发动机&#xff0c;车辆能…

安卓玩机工具-----ADB与 FASTBOOT模式 图形化 多功能玩机刷机工具

工具说明 这款工具是英文版。易于使用的工具提供了用于运行 ADB 和 Fastboot 命令的图形用户界面。ADB 功能包括旁加载、安装和卸载应用程序、测试设备以及重新启动到不同的模式。可以使用 fastboot 命令进行设备管理;其中包括检查 Antirollback 和 active slots 等变…

YOLOv8 人体姿态估计动作识别关键点检测(代码+教程)

YOLOv8 人体姿态判断 项目介绍 YOLOv8 人体姿态判断 是一个基于最新YOLOv8模型的深度学习项目&#xff0c;旨在识别和分析人体姿态。该项目利用先进的计算机视觉技术和深度学习框架&#xff0c;通过摄像头捕捉实时图像或处理存储图像&#xff0c;识别人体的关键点&#xff0c…

在线查看 Android 系统源代码 AOSPXRef and AndroidXRef

在线查看 Android 系统源代码 AOSPXRef and AndroidXRef 1. AOSPXRef1.1. http://aospxref.com/android-14.0.0_r2/1.2. build/envsetup.sh 2. AndroidXRef2.1. http://androidxref.com/9.0.0_r3/2.2. build/envsetup.sh 3. HELLO AndroidReferences 1. AOSPXRef http://aospx…

加密与安全_优雅存储用户密码的最佳实践

文章目录 Pre概述最佳实践避免使用MD5、SHA1等快速哈希算法加盐哈希 &#xff08;不推荐&#xff09;使用BCrypt、Argon2等慢哈希算法 (推荐)BCrypt Code1. 自动生成和嵌入盐2. 哈希结果的格式3. 代价因子 BCrypt特点 防止暴力破解1. 登录失败锁定2. 双因素认证&#xff08;2FA…

Golang | Leetcode Golang题解之第409题最长回文串

题目&#xff1a; 题解&#xff1a; func longestPalindrome(s string) int {mp : map[byte]int{}for i : 0; i < len(s); i {mp[s[i]]}res : 0for _, v : range mp {if v&1 1 {res v - 1} else {res v}}if res<len(s) {res}return res }

Python酷库之旅-第三方库Pandas(117)

目录 一、用法精讲 516、pandas.DataFrame.add_suffix方法 516-1、语法 516-2、参数 516-3、功能 516-4、返回值 516-5、说明 516-6、用法 516-6-1、数据准备 516-6-2、代码示例 516-6-3、结果输出 517、pandas.DataFrame.align方法 517-1、语法 517-2、参数 51…

Go语言基本语法

Go语言&#xff08;通常称为Golang&#xff09;是由Google开发的一种静态类型、编译型语言&#xff0c;它旨在简化系统编程、网络编程和并发编程的复杂性。 Go语言以其简洁、高效和易于理解的语法而受到开发者的喜爱。 Go语言的一些基本语法元素&#xff1a; 1. 包&#xff…

protobuf中c、c++、python使用

文章目录 protobuf实例&#xff1a;例题1&#xff1a;[CISCN 2023 初赛]StrangeTalkBot分析&#xff1a;思路&#xff1a;利用&#xff1a; 例题2&#xff1a;[CISCN 2024]protoverflow分析&#xff1a; protobuf Protocol Buffers&#xff0c;是Google公司开发的一种数据描述语…

数学学习记录

9月14日 1.映射&#xff1a; 2.函数: 9月15日 3.反函数&#xff1a; 4.收敛数列的性质 5.反三角函数&#xff1a; 9月16日 6.函数的极限&#xff1a; 7.无穷小和无穷大 极限运算法则&#xff1a;

远程Linux网络连接( Linux 网络操作系统 04)

接下来我们准备开始进入Linux操作系统的第二个模块的学习&#xff0c;不过在学习之前我们需要对如下进行简单的配置&#xff0c;通过外接辅助软件MobaXterm来进行虚拟操作系统的访问。接下来的课程我们会一直在MobaXterm中进行命令和相关知识的学习。 一、准备阶段 1.1 软件 …

学习笔记JVM篇(三)

一、垃圾回收机制 垃圾回收&#xff08;Garbage Collection&#xff09;机制&#xff0c;是自动回收无用对象从而释放内存的一种机制。Java之所以相对简单&#xff0c;很大程度是归功于垃圾回收机制。&#xff08;例如C语言申请内存后要手动的释放&#xff09; 优点&#xff…

数据清洗-缺失值填充-K-NN算法(K-Nearest Neighbors, K-NN算法)

目录 一、安装所需的python包二、采用K-NN算法进行缺失值填充2.1可直接运行代码2.2以某个缺失值数据进行实战2.2.1代码运行过程截屏&#xff1a;2.2.2填充后的数据截屏&#xff1a; 三、K 近邻算法 (K-Nearest Neighbors, KNN) 介绍3.1 K 近邻算法定义3.2 K 近邻算法的基本思想…

福建科立讯通信 指挥调度管理平台 SQL注入漏洞

北峰通信-福建科立讯通信 指挥调度管理平台 SQL注入漏洞 厂商域名和信息收集 域名&#xff1a; 工具sqlmap python sqlmap.py -u "http://ip:端口/api/client/down_file.php?uuid1" --batch 数据包 GET /api/client/down_file.php?uuid1%27%20AND%20(SELECT%20…

替换 Oracle ,江河信息用 TDengine 解决高基数查询写入问题

在数字经济快速发展的背景下&#xff0c;智慧水利作为重要的基础设施之一&#xff0c;正逐步成为提升水资源管理效率、优化生态环境的重要力量。江西省水投江河信息技术有限公司&#xff08;以下简称“江河信息”&#xff09;作为高新技术国有企业&#xff0c;坚定致力于打造数…

Leetcode 每日一题:Longest Increasing Path in a Matrix

写在前面&#xff1a; 今天我们继续看一道 图论和遍历 相关的题目。这道题目的背景是在一个矩阵当中找寻最长的递增数列长度。思路上非常好想&#xff0c;绝对和 DFS 相关&#xff0c;但是题目的优化要求非常高&#xff0c;对于语言和内存特性的考察特别丰富&#xff0c;如果是…

15. Springboot集成Redis

目录 1、前言 2、为什么选择Spring Boot集成Redis&#xff1f; 3、快速上手 3.1、引入依赖 3.2、 配置连接信息 3.3、自定义配置类 4、RedisTemplate的使用 4.1、String类型操作 4.2、 Hash类型操作 4.3、List类型操作 4.4、Set类型操作 4.5、SortedSet类型操作 4…

第十一章 【后端】商品分类管理微服务(11.2)——Lombok

11.2 Lombok 官网:https://projectlombok.org/ 较新版本的 idea 已默认安装 lombok 插件 Lombok 工具提供一系列的注解,使用这些注解可以不用定义 getter、setter、equals、constructor 等,可以消除 java 代码的臃肿,编译时它会在字节码文件中自动生成这些通用的方法,简…