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

什么是栈溢出

一、什么是栈溢出

       栈溢出(Stack Overflow)就是指在程序运行过程中,往栈里存放的数据超过了栈所能容纳的最大容量,从而导致程序出现异常行为的情况。这就好比一个箱子本来只能装一定数量的物品,硬要往里面塞更多的东西,最后东西就会 “溢出来”,影响到箱子周围的其他物品甚至整个存放系统的正常运作。

       在计算机程序的运行时内存布局中,栈是一种用于存储局部变量、函数调用信息(如返回地址、参数等)的数据结构。它按照后进先出(LIFO)的原则进行操作。当程序不断地向栈中压入数据(比如不断地调用函数,每个函数调用都会在栈上分配空间用于存储局部变量和调用相关信息),而没有及时进行出栈操作释放空间,一旦栈的使用超过了其所能容纳的最大范围,就会发生栈溢出。

 二、什么情况下会栈溢出(以 C 语言举例)

(一)递归调用过深

       当一个函数在其内部不断地调用自身,并且没有合理的终止条件或者终止条件很难达到时,就会导致栈不断地增长,最终可能引发栈溢出。以下是一个计算阶乘的递归函数示例,当输入的数值较大时,就可能出现栈溢出的情况:

#include <stdio.h>int factorial(int n) {if (n == 0 || n == 1) {return 1;}return n * factorial(n - 1);
}int main() {int result = factorial(10);  // 如果这里传入一个较大的值,可能导致栈溢出printf("阶乘结果: %d\n", result);return 0;
}

       在上述代码中,当调用factorial函数时,每一次递归调用都会在栈上为当前函数调用的局部变量(这里主要就是参数n)以及返回地址等信息分配空间。如果传入一个较大的n值,递归次数过多,栈空间就会被耗尽,从而引发栈溢出。

(二)局部变量占用过多栈空间

       如果在一个函数内部定义了大量的局部变量,尤其是一些占用较大内存空间的变量,比如大型数组等,就容易使栈空间快速被占用,进而导致栈溢出。

#include <stdio.h>void functionWithLargeArray() {int largeArray1[10000];  // 定义几个较大的数组作为局部变量int largeArray2[10000]; int largeArray3[10000]; // 其他代码逻辑
}int main() {functionWithLargeArray();return 0;
}

       在这个例子中,functionWithLargeArray函数内部定义了三个能容纳 10000 个整数的数组largeArray作为局部变量。当调用这个函数时,栈需要为这些大型数组分配足够的空间,如果栈的剩余空间不足,就会发生栈溢出。

(三)无限循环中的函数调用

       在一个无限循环体内不断地进行函数调用,且每个函数调用都会在栈上分配空间,这样持续下去也会导致栈溢出。

#include <stdio.h>void someFunction() {// 这里可以有一些函数内部的逻辑,每次调用会在栈上分配空间
}int main() {while (1) {someFunction();}return 0;
}

       在上述代码中,main函数中的无限循环会不断地调用someFunction函数,而每次调用someFunction函数都会在栈上为其分配空间用于存放局部变量、返回地址等信息,随着循环的不断进行,栈空间最终会被耗尽,引发栈溢出。

 三、如何避免栈溢出

(一)优化递归算法

       对于因递归调用过深导致栈溢出的情况,可以通过优化递归算法来解决。

       1.1、尾递归优化:如果编译器支持尾递归优化,可以将递归函数改写成尾递归的形式。尾递归是指递归调用是函数的最后一个操作,这样编译器可以将其优化为迭代形式,从而避免栈的过度增长。

       例如,将前面计算阶乘的递归函数改写成尾递归形式如下:

#include <stdio.h>int factorialTail(int n, int accumulator) {if (n == 0 || n == 1) {return accumulator;}return factorialTail(n - 1, n * accumulator);
}int main() {int result = factorialTail(10, 1);printf("阶乘结果: %d\n", result);return 0;
}

       在这个尾递归版本的函数中,每次递归调用时,不需要保存当前函数调用的额外状态信息在栈上,编译器可以将其优化为类似迭代的形式,从而大大减少了栈的使用量。

       1.2、采用迭代方式替代递归:很多情况下,也可以直接将递归算法改写成迭代算法,完全避免了递归调用栈的积累。

       例如,再次改写计算阶乘的函数为迭代形式:

#include <stdio.h>int factorialIterative(int n) {int result = 1;for (int i = 1; i <= n; i++) {result *= i;}return result;
}int main() {int result = factorialIterative(10);printf("阶乘结果: %d\n", result);return 0;
}

       这种迭代形式的函数在计算阶乘时,只使用了有限的栈空间(主要就是用于存放函数内部的局部变量result和i),不会因为递归调用而导致栈溢出。

(二)合理管理局部变量

       尽量减少在函数内部定义大量占用空间的局部变量。如果确实需要使用较大的数据结构,可以考虑将其定义在堆空间(通过动态内存分配机制,如malloc等),而不是栈空间。

       例如,对于前面定义大型数组导致栈溢出的例子,可以将其修改为如下形式:

#include <stdio.h>void functionWithLargeArray() {int *largeArray = (int *)malloc(sizeof(int) * 10000);// 其他逻辑代码free(largeArray);
}int main() {functionWithLargeArray();return 0;
}

       在这个修改后的代码中,通过malloc函数在堆空间动态分配了一个能容纳 10000 个整数的数组largeArray,这样就不会占用过多的栈空间,避免了因局部变量占用过多栈空间而导致的栈溢出。

(三)检查循环逻辑

       确保在循环体内进行函数调用等操作时,循环是有终止条件的,并且不会因为逻辑错误导致无限循环从而引发栈溢出。在编写代码时要仔细分析循环的终止条件是否合理,以及是否会出现意外情况导致循环无法正常终止。

       比如,对于前面无限循环中函数调用导致栈溢出的例子,可以将其修改为如下有终止条件的循环:

#include <stdio.h>void someFunction() {//其他逻辑代码
}int main() {for (int i = 0; i < 10; i++) {someFunction();}return 0;
}

        在这个修改后的代码中,通过设置循环的终止条件为i < 10,使得循环只会执行有限次,每次调用someFunction函数也只会在栈上分配有限的空间,从而避免了栈溢出。

 四、栈溢出会有什么严重的后果

(一)程序崩溃

       栈溢出最常见的后果就是导致程序异常终止,也就是崩溃。当栈溢出发生时,程序可能会因为访问到了非法的内存区域(超出了栈的范围)而触发操作系统的保护机制。

       在 Windows 系统中,可能会弹出 “程序已停止工作” 的提示;在 Linux 系统中,可能会生成相应的错误核心文件,并且程序进程会终止运行,无法继续完成预期的任务。

(二)数据损坏

       由于栈溢出可能会导致内存中的数据被意外修改,尤其是栈附近的其他数据结构(如其他函数的局部变量、函数调用的返回地址等)可能会受到影响。这就有可能导致程序后续的运行出现错误的结果,比如计算结果错误、逻辑判断失误等,因为相关的数据已经被不正确地改变了。

相关文章:

什么是栈溢出

一、什么是栈溢出 栈溢出&#xff08;Stack Overflow&#xff09;就是指在程序运行过程中&#xff0c;往栈里存放的数据超过了栈所能容纳的最大容量&#xff0c;从而导致程序出现异常行为的情况。这就好比一个箱子本来只能装一定数量的物品&#xff0c;硬要往里面塞更多的东西&…...

在linux中arm-linux-gcc和/usr/bin/gcc有啥区别

在Linux中&#xff0c;arm-linux-gcc和/usr/bin/gcc都是编译器&#xff0c;但它们之间存在显著的区别&#xff0c;主要体现在编译目标、使用场景以及编译生成的二进制文件的可执行性上。而软链接则是Linux文件系统中的一种特殊文件类型&#xff0c;用于创建一个文件的别名。 a…...

常用环境部署(二十二)——MySQL的数据库迁移到另一个机器上

1、导出原数据库的数据 mysqldump -u [用户名] -p[密码] [数据库名] > database_dump.sql 命令示例&#xff1a; mysqldump -u root -p123456 wd > /opt/wd.sql 2、在新机器上创建数据库 mysql -u [用户名] -p -e "CREATE DATABASE [新数据库名]" 命令示…...

两台主机只能单方向ping通

可能性比较大的原因时ping不通的那台主机安装了个人防火墙。 在共享上网的机器中&#xff0c;出于安全考虑&#xff0c;大部分主机都安装个人防火墙软件。几乎所有个人防火墙软件默认不允许其他机器ping本机。一般的做法是将来自外部的ICMP请求报文滤掉&#xff0c;对本机出去的…...

redis windows 5.0 下载

Redis 简介 Redis 是一个高性能的 key-value 数据库&#xff0c;广泛应用于缓存、消息队列、实时分析等场景。它支持多种数据结构&#xff0c;如字符串、哈希、列表、集合、有序集合等&#xff0c;并且提供了丰富的操作命令&#xff0c;能够满足各种复杂的数据处理需求。 下载…...

视频转gif怎么转换?6种视频格式转换简单方法分享,附操作截图!

gif动图凭借其简洁而生动的特点&#xff0c;已成为互联网交流中不可或缺的一部分。尽管gif和视频在技术上有所不同&#xff0c;但两者都能以短小的帧展现动作&#xff0c;而gif通常不带声音&#xff0c;具备循环播放的特性。因此&#xff0c;出于创建gif动图、存储更多媒体文件…...

StructRAG简介

StructRAG是一种新型的框架&#xff0c;旨在提升大型语言模型&#xff08;LLMs&#xff09;在知识密集型推理任务中的性能。它通过推理时的混合信息结构化机制&#xff0c;根据任务需求以最合适的格式构建和利用结构化知识。 以下是StructRAG的核心组成部分和工作流程&#xff…...

java脚手架系列12-mongoDB

之所以想写这一系列&#xff0c;是因为之前工作过程中有几次项目是从零开始搭建的&#xff0c;而且项目涉及的内容还不少。在这过程中&#xff0c;遇到了很多棘手的非业务问题&#xff0c;在不断实践过程中慢慢积累出一些基本的实践经验&#xff0c;认为这些与业务无关的基本的…...

python四舍五入保留两位小数

在 Python 中&#xff0c;你可以使用内置的 round() 函数来对数字进行四舍五入并保留两位小数。round() 函数有两个参数&#xff1a;要四舍五入的数字和要保留的小数位数。以下是一个简单的示例&#xff1a; # 示例数字 number 3.14159# 四舍五入保留两位小数 rounded_number…...

期权懂|有什么期权交易策略能够稳赚不赔的?

期权小懂小编每日分享期权知识&#xff0c;帮助期权新手及时有效地掌握即市趋势与新资讯&#xff01; 有什么期权交易策略能够稳赚不赔的? 期权交易具有风险性&#xff0c;没有任何一种策略能够保证稳赚不赔。 以下是一些常见的期权交易策略&#xff0c;虽不能保证盈利&#…...

笔记本脱机状态

先是显示脱机&#xff0c;请尝试其他方法登录 1.按照联想客服&#xff0c;进入高级选项里面&#xff0c;清除两个更新项目&#xff0c;没有卸载成功 2.安装wepe&#xff0c;先是能检测到U盘&#xff0c;但是进不去&#xff0c;然后我淘宝淘帮我做盘&#xff0c;我自己重新装了一…...

Node.js:模块 包

Node.js&#xff1a;模块 & 包 模块module对象 包npm安装包配置文件镜像源 分类 模块 模块化是指解决一个复杂问题时&#xff0c;自顶向下逐层把系统划分成若干模块的过程。对于整个系统来说&#xff0c;模块是可组合、分解和更换的单元。 简单来说&#xff0c;就是把一个…...

油动无人机动力测试台-60公斤级-Flight Stand 60 ICE

产品简介 通过Flight Stand 60 ICE测试台对内燃机和螺旋桨的拉力&#xff0c;扭矩&#xff0c;转速&#xff0c;燃油流量&#xff0c;温度&#xff0c;功率和螺旋桨效率的测量&#xff0c;帮助用户精准地描述和评估其性能参数&#xff0c;以不断地优化和提升燃油动力系统性能。…...

给grasshopper中的python脚本电池加个标签

ghenv.Component.Message test使用python脚本创建的电池&#xff0c;也可以保存起来。 File – Create User Object...

别被忽悠了 Lua 数组真的也可以从 0 开始索引?

先前我说 Lua 数组从 1 开始不太爽&#xff0c;很多人来纠正我说也可以从 0 开始&#xff0c;比如&#xff1a; local m { [0] 100, 101, 102, 103 }然后访问时 m[0] 也可以正常访问到第 0 个元素&#xff0c;所以 “Lua 给你充分自由度&#xff0c;让你可以从任意下标索引数…...

docker占用磁盘过多问题

我在windows系统上用docker&#xff0c;安装在C盘环境下&#xff0c;我发现C盘占用了大量的空间&#xff0c;查找后发现是docker的映像文件占用的&#xff0c;于是开始清理&#xff0c;中间还踩个坑&#xff0c;记录一下&#xff0c;下次需要的时候方便找。 踩坑 我本想移动映…...

[实时计算flink]使用Python依赖

您可以在Flink Python作业中使用自定义的Python虚拟环境、第三方Python包、JAR包和数据文件等&#xff0c;本文为您介绍如何在Python作业中使用这些依赖。 使用自定义的Python虚拟环境 说明 VVR 4.x仅支持3.7版本的Python虚拟环境&#xff0c;VVR 6.x及以上的版本无此限制&a…...

MySql如何实现分布式锁

本篇我们使用mysql实现一个分布式锁。 环境&#xff1a;mysql8,navicat,maven,springboot2.3.11,mybatis-plus 分布式锁的功能 1,分布式锁使用者位于不同的机器中&#xff0c;锁获取成功之后&#xff0c;才可以对共享资源进行操作 2,锁具有重入的功能&#xff1a;即一个使用…...

「行内揭秘」 SQLynx数据库界的“小众宝藏”?

数据库界的“小众宝藏”&#xff1f;Navicat老大哥地位稳如泰山&#xff0c;但这位“SQLynx”小弟也不容小觑&#xff01;&#x1f440; 别看它小众&#xff0c;SQLynx在处理数据库事务上那可是丝毫不含糊&#xff0c;无论你是Windows Linux和Mac&#xff0c;甚至银河麒麟统信都…...

【已解决】【MySQL】IDEA配置数据库 报错 未配置SQL方言 无法使用SQL提示

IDEA配置数据库的步骤 下载插件 添加数据源 新建--->选择数据源MySQL 页面展示&#xff1a; 主机名&#xff1a;一般都是localhost不用改端口&#xff1a;填写自己的端口号用户&#xff1a;填写自己的用户名密码&#xff1a;填写自己设置的密码数据库&#xff1a;填写需要…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

Web后端基础(基础知识)

BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 优点&#xff1a;维护方便缺点&#xff1a;体验一般 CS架构&#xff1a;Client/Server&#xff0c;客户端/服务器架构模式。需要单独…...

9-Oracle 23 ai Vector Search 特性 知识准备

很多小伙伴是不是参加了 免费认证课程&#xff08;限时至2025/5/15&#xff09; Oracle AI Vector Search 1Z0-184-25考试&#xff0c;都顺利拿到certified了没。 各行各业的AI 大模型的到来&#xff0c;传统的数据库中的SQL还能不能打&#xff0c;结构化和非结构的话数据如何和…...

数据结构:递归的种类(Types of Recursion)

目录 尾递归&#xff08;Tail Recursion&#xff09; 什么是 Loop&#xff08;循环&#xff09;&#xff1f; 复杂度分析 头递归&#xff08;Head Recursion&#xff09; 树形递归&#xff08;Tree Recursion&#xff09; 线性递归&#xff08;Linear Recursion&#xff09;…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用

前言&#xff1a;我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM&#xff08;Java Virtual Machine&#xff09;让"一次编写&#xff0c;到处运行"成为可能。这个软件层面的虚拟化让我着迷&#xff0c;但直到后来接触VMware和Doc…...