C++ stack overflow 局部数组变量定义超过所分配的最大空间

本文详细介绍了C++程序中栈溢出的原因,并提供了通过调整堆栈保留大小来解决该问题的方法。具体步骤包括在项目属性中设置链接器系统的堆栈保留大小,从而允许程序正确运行并避免栈溢出错误。

局部数组变量定义所分配的最大空间为多少?如何设置大小

有两个程序
A:

  1. #include "stdafx.h"
  2. int _tmain(int argc, _TCHAR* argv[])
  3. {
  4. int nArray[256000] = {0};
  5. nArray[1] = 5;
  6. printf("array 1 is %d",nArray[1]);
  7. return 0;
  8. }

B:

  1. #include "stdafx.h"
  2. int _tmain(int argc, _TCHAR* argv[])
  3. {
  4. int nArray[260000] = {0};
  5. nArray[1] = 5;
  6. printf("array 1 is %d",nArray[1]);
  7. return 0;
  8. }

大家通过运行可以发现,A是可以正常运行的,B虽然编译通过了,可是当运行时就会弹出错误

错误的原因,就是栈溢出
局部变量的申请空间是存放于栈中,windows里默认栈内存是1M

所以当申请空间大于1M时就会出现溢出错误

通过debug就会进入以下文件chkask.asm

page ,132
title chkstk – C stack checking routine
;***
;chkstk.asm – C stack checking routine
;
; Copyright (c) Microsoft Corporation. All rights reserved.
;
;Purpose:
; Provides support for automatic stack checking in C procedures
; when stack checking is enabled.
;
;*******************************************************************************
.xlist
include cruntime.inc
.list
; size of a page of memory
_PAGESIZE_ equ 1000h

CODESEG
page
;***
;_chkstk – check stack upon procedure entry
;
;Purpose:
; Provide stack checking on procedure entry. Method is to simply probe
; each page of memory required for the stack in descending order. This
; causes the necessary pages of memory to be allocated via the guard
; page scheme, if possible. In the event of failure, the OS raises the
; _XCPT_UNABLE_TO_GROW_STACK exception.
;
; NOTE: Currently, the (EAX < _PAGESIZE_) code path falls through
; to the "lastpage" label of the (EAX >= _PAGESIZE_) code path. This
; is small; a minor speed optimization would be to special case
; this up top. This would avoid the painful save/restore of
; ecx and would shorten the code path by 4-6 instructions.
;
;Entry:
; EAX = size of local frame
;
;Exit:
; ESP = new stackframe, if successful
;
;Uses:
; EAX
;
;Exceptions:
; _XCPT_GUARD_PAGE_VIOLATION – May be raised on a page probe. NEVER TRAP
; THIS!!!! It is used by the OS to grow the
; stack on demand.
; _XCPT_UNABLE_TO_GROW_STACK – The stack cannot be grown. More precisely,
; the attempt by the OS memory manager to
; allocate another guard page in response
; to a _XCPT_GUARD_PAGE_VIOLATION has
; failed.
;
;*******************************************************************************
public _alloca_probe
_chkstk proc
_alloca_probe = _chkstk
push ecx
; Calculate new TOS.
lea ecx, [esp] + 8 – 4 ; TOS before entering function + size for ret value
sub ecx, eax ; new TOS
; Handle allocation size that results in wraparound.
; Wraparound will result in StackOverflow exception.
sbb eax, eax ; 0 if CF==0, ~0 if CF==1
not eax ; ~0 if TOS did not wrapped around, 0 otherwise
and ecx, eax ; set to 0 if wraparound
mov eax, esp ; current TOS
and eax, not ( _PAGESIZE_ – 1) ; Round down to current page boundary
cs10:
cmp ecx, eax ; Is new TOS
jb short cs20 ; in probed page?
mov eax, ecx ; yes.
pop ecx
xchg esp, eax ; update esp
mov eax, dword ptr [eax] ; get return address
mov dword ptr [esp], eax ; and put it at new TOS
ret
; Find next lower page and probe
cs20:
sub eax, _PAGESIZE_ ; decrease by PAGESIZE
test dword ptr [eax],eax ; probe page.
jmp short cs10
_chkstk endp
end

提示栈溢出

所以解决此问题的方法就是扩大栈空间的大小

方法为

项目->属性->链接器->系统->堆栈保留大小

注:这里填的是字节数
如果你想把他扩大为2M的话,
1024*1024*2 = 2097152

然后再编译运行的话A,B就都可以通过了


局部变量是在 **栈(Stack)** 上分配的,具体来说是分配在函数对应的 **栈帧(Stack Frame)** 中。 这是现代程序运行时内存管理的核心机制之一。下面我们从多个角度详细解释局部变量是如何分配、存储和释放的。 --- ## ✅ 一、局部变量定义 局部变量是指在函数内部或代码块中声明的变量,其作用域仅限于该函数或代码块。 ```c void func(int a) { int x = 10; // 局部变量 char buf[32]; // 局部数组也是局部变量 double d = 3.14; } ``` 这些变量都在函数执行期间创建,在函数返回后自动销毁 —— 这背后的关键就是 **栈空间分配**。 --- ## ✅ 二、局部变量的存储位置:栈(Stack) ### 1. 栈的基本结构 程序运行时,每个线程都有一个 **调用栈(Call Stack)**,用于管理函数调用。每当一个函数被调用时,系统会在栈上为其分配一块内存区域,称为 **栈帧(Stack Frame)**。 一个典型的栈帧布局如下(从高地址到低地址): ```text 高地址 +---------------------+ | 调用者的局部变量 | +---------------------+ | 参数 n | ← 入口参数 | ... | | 参数 1 | +---------------------+ | 返回地址 | ← LR(由 BL 指令保存) +---------------------+ | 保存的寄存器 | ← 如 R4-R7, LR 等 +---------------------+ | 局部变量 | ← 就在这里! +---------------------+ | 临时空间(如数组) | +---------------------+ ← SP(Stack Pointer)当前指向这里(低地址) 低地址 ``` > ⚠️ 注意:ARM 架构中栈通常是从 **高地址向低地址增长** 的。 --- ### 2. 示例:C 函数与汇编对应关系 ```c void func(int a) { int x = a + 1; // 局部变量 x char buffer[16]; // 局部数组 buffer x += 2; } ``` 编译成 ARM 汇编后大致为: ```armasm func: PUSH {LR} ; 保存返回地址 SUB SP, SP, #20 ; 分配 20 字节栈空间(x:4字节 + buffer:16字节) LDR R1, [R0] ; 假设 a 在 R0,加载参数 ADD R1, R1, #1 ; x = a + 1 STR R1, [SP, #16] ; 将 x 存入栈中偏移 16 处 ; 使用 buffer(位于 [SP] 到 [SP+15]) ; ... ADD R1, R1, #2 ; x += 2 STR R1, [SP, #16] ADD SP, SP, #20 ; 释放栈空间(回收局部变量) POP {PC} ; 返回调用者 ``` 📌 关键点: - `SUB SP, SP, #20`:通过移动栈指针来“分配空间 - 局部变量通过 `[SP + offset]` 地址访问 - 函数结束前用 `ADD SP, SP, #20` 回收空间(无需清零,下次会覆盖) --- ## ✅ 三、为什么局部变量放在栈上? | 优点 | 说明 | |------|------| | ✅ 自动分配/释放 | 函数进入时分配,返回时自动回收(栈指针移动即可) | | ✅ 高效 | 只需调整 `SP` 寄存器,速度快于堆分配 | | ✅ 支持递归 | 每次调用都创建独立栈帧,互不干扰 | | ✅ 安全性 | 不同函数的局部变量隔离,避免冲突 | --- ## ✅ 四、特殊情况下的局部变量分配方式 虽然大多数局部变量都在栈上,但也有例外: ### 1. **寄存器变量(Register Variables)** 程序员可以建议编译器将变量放在寄存器中(现代编译器自动优化): ```c register int i = 0; // 建议使用寄存器(可选) ``` 或者编译器自行决定: ```c int temp = a + b; // 编译器可能直接用 R4 存储 temp,而不进栈 ``` ✅ 特点:更快,不占用栈空间。 --- ### 2. **静态局部变量(static local variables)** ```c void counter() { static int count = 0; // 静态局部变量 count++; } ``` ❌ 不在栈上! ✅ 分配在 **数据段(.data 或 .bss)**,生命周期贯穿整个程序运行期。 > 类似全局变量,只是作用域限制在函数内。 --- ### 3. **大型数组或动态大小数组(VLA)** ```c void large_func(int n) { int arr[n * 1000]; // 变长数组(VLA) } ``` 这类变量也可能被分配在栈上,但如果太大,可能导致 **栈溢出(Stack Overflow)**。 ⚠️ 建议:大对象应使用堆分配(`malloc`)。 --- ### 4. **被闭包捕获的局部变量(如 Lambda 表达式)** 在 C++、Python 等语言中,如果局部变量被 lambda 或嵌套函数引用,可能会被提升到堆上: ```cpp auto make_counter() { int count = 0; return [&]() { return ++count; }; // 引用捕获,可能堆分配 } ``` 此时变量不再在栈上,而是随闭包一起分配在堆中。 --- ## ✅ 五、栈溢出风险示例 ```c void dangerous() { char huge[1024 * 1024]; // 1MB 局部数组! // 很可能导致栈溢出(默认栈大小通常只有几 MB) } ``` 💡 解决方案: ```c void safe() { char *huge = malloc(1024 * 1024); // 改用堆分配 free(huge); } ``` --- ## ✅ 六、总结:局部变量分配规则 | 变量类型 | 分配位置 | 生命周期 | 是否自动释放 | |--------|----------|-----------|---------------| | 普通局部变量(int, char[] 等) | 栈(Stack) | 函数调用期间 | ✅ 是 | | `register` 变量 / 编译器优化变量 | 寄存器(Register) | 函数期间 | ✅ 是 | | `static` 局部变量 | 数据段(.data/.bss) | 程序全程 | ❌ 否 | | VLA(变长数组) | 栈(若小),否则危险 | 函数期间 | ✅ 是(但仍可能溢出) | | 被闭包捕获的变量 | 堆(Heap) | 闭包存活期间 | 手动或垃圾回收 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值