当前位置: 首页 > news >正文

动态内存操作(2)

接上一篇文章http://t.csdn.cn/1ONDq,这次我们继续讲解关于动态内存的相关知识。

一、常见的动态内存错误

1.对NULL指针进行解引用操作

#include<stdio.h>
#include<stdlib.h>
#include<limits.h>
int main()
{int* p = (int*)malloc(INT_MAX/4);*p = 20;//如果没有足够的空间导致p为NULL,就会有问题//所以必须对malloc的返回值进行判断free(p);p = NULL;return 0;
}

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

int main()
{int* p = (int*)malloc(40);if (p == NULL){perror("malloc");return 1;}int i = 0;for (i = 0; i < 11; i++){p[i] = 0;//原本只申请了十个整型的空间,但却访问十一个整型//所以造成越界访问}free(p);p = NULL;return 0;
}

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


int main()
{int a = 0;int* p = &a;free(p);//p不是动态开辟的空间,不能释放p = NULL;return 0;
}

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

int main()
{int* p = (int*)malloc(40);if (p == NULL){perror("malloc");return 1;}int i = 0;for (i = 0; i < 5; i++){p[i] = 0;p++;}//p++导致p不再指向这块空间的起始地址//所以如果释放p,等于释放这块空间的一部分(后五个整型空间)//这样就会出问题free(p);p = NULL;return 0;
}

5.对同一快动态内存多次释放

int main()
{int* p = (int*)malloc(40);if (p == NULL){perror("malloc");return 1;}free(p);//。。。。。free(p);//有时候头脑不清醒就可能释放多次,这样就会出问题return 0;
}

6.动态开辟内存后忘记释放内存(最常见)

即我们动态申请内存后,最后忘记用free释放了,这样就会造成内存泄漏

二、几个关于动态内存的经典例题

例题1、代码运行结果是什么?

源代码:

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

问:这段代码运行结果会是什么呢?

例题1解答:

一是没有释放动态内存;

二是会产生这样的错误:

原因是:因为在Test函数中,把str指针置空,然后作为参数传给GesMemory函数,该函数形参用指针p接收,这样指针p也为NULL,然后给指针p动态开辟空间,函数结束,到strcpy函数,但我们注意,我们是给指针p开辟空间,但指针str是没有变的,很多伙伴想不清楚,这不是传址调用吗,p指针变了,str指针也应该跟着变呀?

实则不然,我们应该注意参数是指针也不一定是传址调用,这里是指针之间赋值,应该同时上升一段层次,这里要二级指针才算传址调用,所以指针str是不会变的,还是NULL,既然是NULL,所以就没有足够的空间能放下strcpy的第二个参数,所以报错。

例题2、代码运行结果是什么?

char* GetMemory(void)
{char p[] = "hello world";return p;
}
void Test(void)
{char* str = NULL;str = GetMemory();printf(str);
}int main()
{Test();return 0;
}

问:这段代码运行结果是什么?

例题2解答:

会产生这样的结果:

“很多小伙伴可能觉得,在GetMemory函数里面返回字符串的起始地址p,所以在Test函数里面用指针str来接收并打印,所以运行结果应该为打印字符串。”

但实则不然,我们一定要注意每个变量的生命周期,数组p的生命周期就只在函数GetMemory里面,所以当该函数return后,里面的变量所占的空间都会被自动销毁(释放),既然p的空间已经被释放了,还赋值给指针str,所以str就是个野指针,再打印str,就造成非法访问内存了。

这类问题属于:返回栈空间地址的问题

三、C/C++程序的内存开辟

如下图:

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

四、柔性数组

(一)、柔性数组的概念:

也许你从来没有听说过 柔性数组( flexible array 这个概念,但是它确实是存在的。
C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。
如下:
//定义一:
struct S
{int a;int arr[0];
};
//定义二:
struct B
{int a;char b;int arr[];
};

(二)、柔性数组的特点:

1、 结构中的柔性数组成员前面必须至少一个其他成员。
2、 sizeof 返回的这种结构大小不包括柔性数组的内存,即sizeof只计算柔性数组前面的成员的大小。
3、 包含柔性数组成员的结构用 malloc () 函数进行内存的动态分配,并且分配的内存应该大于结构的大 小,以适应柔性数组的预期大小。

(三)、柔性数组的使用:

如下例:
struct S
{int a;int arr[];
};int main()
{//动态开辟了4+40个字节,因为柔性数组是不会被sizeof计算的//前面四个字节是给成员a的,后面四十个字节给柔性数组//因为柔性数组的大小是未知的,我们只需给出预期大小struct S* str = (struct S*)malloc(sizeof(struct S) + 40);//检查if (str == NULL){perror("malloc");return 1;}//使用str->a = 10;int i = 0;for (i = 0; i < 10; i++){str->arr[i] = i + 1;//printf("%d ", str->arr[i]);}//用realloc扩容,因为柔性数组大小未知,是可以改变的//将之前柔性数组的10个字节的大小扩容到15个struct S* p = (struct S*)realloc(str, sizeof(struct S) + 60);//检查if (p == NULL){perror("realloc");return 1;}//使用str = p;for (i = 10; i < 15; i++){str->arr[i] = i + 1;}//打印for (i = 0; i < 15; i++){printf("%d ", str->arr[i]);}//释放free(str);str = NULL;return 0;
}

用malloc函数进行开辟空间,用realloc函数进行扩容,这样数组的大小就是可变的、柔性

的,这就是柔性数组的特点。

(四)、柔性数组的优势:

1.方便内存释放:
如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free 可以释放结构体,但是用户并不知道这个结构体内的成员也需要 free, 所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free 就可以把所有的内存也给释放掉。
2.这样有利于访问速度:
连续的内存有益于提高访问速度,也有益于减少内存碎片。(其实,我个人觉得也没多高了,反正你跑不了要用做偏移量的加法来寻址)。

相关文章:

动态内存操作(2)

接上一篇文章http://t.csdn.cn/1ONDq&#xff0c;这次我们继续讲解关于动态内存的相关知识。 一、常见的动态内存错误 1.对NULL指针进行解引用操作 #include<stdio.h> #include<stdlib.h> #include<limits.h> int main() {int* p (int*)malloc(INT_MAX/4);…...

Windows-Delphi 窗口置顶

露从今夜白&#xff0c;月是故乡明。 1.Delphi将窗口置顶 if topHwnd <> 0 thenbeginSetWindowPos(topHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE);tmr1.Enabled : True;end; 其中topHwnd是目标窗口的句柄。 2.窗口取消置顶 if topHwnd <> 0 th…...

CVE-2020-11978 Apache Airflow 命令注入漏洞分析与利用

简介 漏洞软件&#xff1a;Apache Airflow影响版本&#xff1a;< 1.10.10 环境 Vulhub 漏洞测试靶场 复现步骤 进入 /root/vulhub/airflow/CVE-2020-11978/ 目录运行以下命令启动环境 # 初始化数据库 docker compose run airflow-init # 开启服务 docker compose up -…...

面试经典算法1:DFS

一、前言 1、题目描述和代码仅供参考&#xff0c;如果有问题欢迎指出 2、解题代码采用acm模式&#xff08;自己处理输入输出&#xff09;&#xff0c;不采用核心代码模式&#xff08;只编程核心函数&#xff09; 3、解题代码采用C语言&#xff08;ai一键翻译任意语言&#xff…...

Windows系统利用cpolar内网穿透搭建Zblog博客网站并实现公网访问内网!

文章目录 1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册 3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 想要成为一个合格的技术宅或程序员&#xff0c;自己搭建网站制作网页是绕…...

SmartCode ViewerX VNC 3.11 Crack

SmartCode ViewerX VNC 查看器 ActiveX 轻松地将 VNC 查看器功能添加到您的应用程序中 SmartCode ViewerX VNC Viewer ActiveX 使开发人员可以使用一组直观的 ActiveX 属性和方法完全访问 VNC 查看器功能。借助ViewerX控件&#xff0c;开发人员可以轻松地为其应用程序提供屏幕共…...

傻瓜式Java操作MySQL数据库备份

文章目录 前言存储数据库存储数据表 前言 数据库备份是开发工作中经常要做的事情&#xff0c;好处是mysql提供了一个非常好的命令 mysqldump&#xff0c;直接调用它就可以将数据以sql文件的形式备份出来。但是直接写命令非常不方便&#xff0c;遇到定时备份或者指定备份那么就需…...

redis常用操作命令

日升时奋斗&#xff0c;日落时自省 注&#xff1a;命令区分有点细&#xff0c;择取自己需要的即可 目录 1、单机架构 2、数据库和应用分离 3、分布式基本概念 3.1、应用&#xff08;Application&#xff09;/系统(System) 3.2、模块&#xff08;Module&#xff09;/组件&…...

pytorch gpu安装

cuda https://blog.csdn.net/qq_51570094/article/details/124148671 https://blog.csdn.net/zxdd2018/article/details/127705627 cudnn https://docs.nvidia.com/deeplearning/cudnn/install-guide/index.html#installlinux-tar 更改cudnn 保证文件目录中只有一个解压后…...

uni跳转页面不缓存上一个页面的方法

一、前言 要实现一个需求&#xff0c;从a页面跳转到b页面&#xff0c;从b页面跳转到c页面&#xff0c;然后按返回&#xff0c;从c页面直接返回a页面&#xff08;不返回b页面&#xff09; a->b->c c->a 二、实现方法 前端框架使用的是uni-app&#xff0c;我们修改…...

排序:败者树和置换选择排序(解决外部排序中的优化问题)

1.算法目的&#xff08;败者树&#xff09; 解决多路平衡归并带来的问题。 在外部排序中&#xff0c;使用k路平衡归并策略, 选出一个最小元素需要对比关键字(k-1)次&#xff0c; 导致内部归并所需时间增加。&#xff08;可用“败者树”进行优化&#xff09; 2.败者树的定义 …...

【超分:光谱响应函数】

Spectral Response Function-Guided Deep Optimization-Driven Network for Spectral Super-Resolution &#xff08;光谱响应函数引导的深度优化驱动网络光谱超分辨&#xff09; 高光谱图像&#xff08;HSI&#xff09;是许多研究工作的关键。光谱超分辨率&#xff08;SSR&a…...

IoT 物联网 JavaScript 全栈开发,构建家居环境监控系统实战

智能家居环境监测端到端场景&#xff0c;全栈JavaScript开发&#xff0c;串联Ruff硬件、温湿度和空气质量传感器、阿里云 IoT、Serverless函数计算、百度ECharts可视化、最终以微信小程序形式在微信里实时展示家中实时温度&#xff0c;湿度&#xff0c;PM2.5指数。 01 技术架构…...

jupyter notebook可以打开,但无法打开.ipynb文件,报错500 : Internal Server Error

1、错误信息 2、解决办法 打开Anaconda Promt界面&#xff0c;进入自己的虚拟环境。在命令行输入以下指令&#xff1a; pip install --upgrade nbconvert...

latex图片编号+表格编号

对编号重新自定义 \renewcommand{\thefigure}{数字编号x}重新命名图的编号\renewcommand{\thetable}{数字编号x}重新命名表的编号编号含义 平时看书经常看到“图1.2”这样的编号&#xff0c;含义是第1章的第2幅插图&#xff1b;或者“图1.1.2”&#xff0c;含义是第1章第1节的…...

【1day】用友时空KSOA平台 imagefield接口SQL注入漏洞学习

注:该文章来自作者日常学习笔记,请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与作者无关。 目录...

linux之美

linux系统和window系统区别 Linux和Windows是两个不同的操作系统。Linux是一个开源操作系统&#xff0c;而Windows是一个商业操作系统。 Linux可以访问源代码并根据用户的需求进行修改&#xff0c;而Windows无法访问源代码。 Linux是免费的&#xff0c;而Windows是商业操作系…...

5、超链接标签

5、超链接标签 超链接标签就是我们常说的a标签 <a href"path" target"目标窗口位置">连接文本或图像</a> <!-- href&#xff08;必填项&#xff09;&#xff1a;连接路径 target&#xff1a;连接在哪个窗口打开&#xff1f;是在新页面打开…...

CCF CSP认证历年题目自练 Day15

CCF CSP认证历年题目自练 Day15 题目一 试题编号&#xff1a; 201709-1 试题名称&#xff1a; 打酱油 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 256.0MB 问题描述&#xff1a; 问题描述   小明带着N元钱去买酱油。酱油10块钱一瓶&#xff0c;商家进行促销&#xf…...

APP的收费模式及特点

移动应用&#xff08;APP&#xff09;的收费模式多种多样&#xff0c;可以根据开发者的需求、目标受众和应用的性质来选择。以下是一些常见的APP收费模式及其特点&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...