栈溢出判断
代码运行过程,经常会出现栈溢出情况,现给定函数调用关系和每个函数的栈大小,判断该程序是否存在栈溢出的风险。
如程序如下:
A()
{
B();
C();
}
B()
{
D();
}
C()
{
E();
}
其中A、B、C、D、E均为函数,A调用了B、C;B调用了D;C调用了E,如果每个函数独立的栈大小分别为:A为10(不包括B、C的栈大小),B为20,C为5,D为15,E为50。调用过程中最大栈空间为路径A->C->E,最大栈空间为65(A->B->D的路径调用最大栈空间为45)。如果整体系统最大额定栈空间配置为60,则A->C->E调用执行时会出现栈溢出异常。
说明:
- 函数名称为单个大写字母,最大不超过26个函数;
- 调用栈为正整数;
- 调用关系不会出现递归调用;
- 最大消耗的栈空间等于系统设定的最大空间,不会发生溢出,只有大于系统最大空间,才会发生溢出。
解答要求
时间限制: C/C++ 1000ms, 其他语言: 2000ms
内存限制: C/C++ 256MB, 其他语言: 512MB
输入格式
输入包括三个信息:每个函数的独立栈大小、函数调用关系、系统配置的最大栈空间。格式如下:
- 第一行:数字N,代表有N个函数
- 第二行到N+1行:表示每个函数名称和栈大小,空格隔开
- 第N+2行:数字M,代表下面M行的函数直接调用关系,第一个字母表示调用函数,后面的字母表示被调用函数,并且是按序调用。如 A B C表示先A->B,然后A->C(如示例所示)
- 最后一行:数字K,代表系统配置的最大栈空间
说明:假设主函数都是从第一个调用关系开始被调用(数字M之后的第一行中第一个函数可以认为是主函数调用口),不会从中间某个函数开始调用。
输出要求
输出信息包括:
- 是否会出现栈溢出
- 如果栈溢出,则输出发生栈溢出的调用路径(路径只输出到触发栈溢出的函数);如果没有栈溢出,则输出最大消耗找的调用路径(如果存在相同大小的调用路径,输出第一个)
- 如果栈溢出,输出发生栈溢出时调用关系的栈大小,如果没有栈溢出,输出最大栈消耗
三个信息一行打印,中间空格隔开:如果栈溢出,只输出第一次触及栈溢出的调用关系即可(实际上如果发生了栈溢出,系统也会异常,不会继续产生后续调用了)。
样例1
输入:6
A 20
B 10
C 5
D 15
E 50
F 60
3
A B C
B D
C E F
70
输出:true A-C-E 75
解释:给定输入,第一个调用关系为A B C,则A为入口函数,先执行A-B-D调用,对应占空间为45,当执行到A-C-E时,超过了70,能力为75能输出。不会再触发后续调用关系了,所以输出为A-C-E,对应的最大空间为75。
样例2
输入:6
A 20
B 10
C 5
D 15
E 50
F 60
3
A B C
B D
C E F
100
输出:false A-C-F 85
解释:该示例中,最大占空间配置为100,可以调用所有函数,不会发生栈溢出。最大调用链路关系为A-C-F,相结合的最大栈空间为85。
理解栈溢出判断题目
这道题目主要涉及函数调用栈和栈空间的概念,以及如何通过分析函数调用关系来判断程序是否会出现栈溢出。下面我们一步步来理解题目的各个部分。
1. 什么是函数调用栈?
函数调用栈是程序在运行过程中用于管理函数调用的一种数据结构。当一个函数被调用时,系统会为这个函数分配一块内存区域,称为栈帧,用于存储该函数的局部变量、参数以及返回地址等信息。多个函数的调用会形成一个调用栈,最新调用的函数位于栈顶,先调用的函数位于栈底。
例如,假设有函数A调用了函数B,函数B又调用了函数C,那么函数调用栈看起来像这样:
栈顶
C
B
A
栈底
2. 什么是栈大小?
每个函数在被调用时,会占用一定的栈空间(即栈大小)。这个栈大小是函数在栈帧中占用的内存量。不同的函数可能需要不同的栈大小,具体取决于函数内部的局部变量数量、参数数量等因素。
在题目中,每个函数都有一个独立的栈大小,例如:
- 函数A的栈大小为10
- 函数B的栈大小为20
- 函数C的栈大小为5
- 函数D的栈大小为15
- 函数E的栈大小为50
3. 什么是栈溢出?
栈溢出是指程序在执行过程中,函数调用栈所占用的内存超过了系统为其分配的最大栈空间。这通常会导致程序崩溃或抛出异常。在这道题目中,我们需要判断在给定的函数调用关系下,是否会发生栈溢出。
4. 如何计算最大栈空间消耗?
要计算程序运行过程中最大的栈空间消耗,我们需要考虑所有可能的函数调用路径,然后计算每条路径上各个函数的栈大小之和。最终,最大的栈空间消耗就是所有路径中最大的那一个。
让我们通过题目中的示例来具体理解:
示例1:
A()
{
B();
C();
}
B()
{
D();
}
C()
{
E();
}
对应的栈大小:
- A: 10
- B: 20
- C: 5
- D: 15
- E: 50
调用关系分析:
-
路径A -> B -> D:
- A的栈大小:10
- B的栈大小:20
- D的栈大小:15
- 总栈空间:10 + 20 + 15 = 45
-
路径A -> C -> E:
- A的栈大小:10
- C的栈大小:5
- E的栈大小:50
- 总栈空间:10 + 5 + 50 = 65
比较两个路径的栈空间消耗:
- A -> B -> D 路径消耗45
- A -> C -> E 路径消耗65
所以最大栈空间消耗为65。
系统配置的最大栈空间为60:
- 由于最大栈空间消耗65 > 系统配置的最大栈空间60,因此会出现栈溢出。
- 具体来说,路径A -> C -> E导致栈空间达到65,超过了60。
输出:
true A-C-E 65
true
表示有栈溢出A-C-E
是发生栈溢出的调用路径65
是触发溢出的栈空间大小
5. 理解题目的输入和输出格式
输入格式:
- 第一行:数字N,代表有N个函数。
- 接下来的N行,每行包含一个函数的名称和其对应的栈大小,用空格分隔。例如:
A 20
表示函数A的栈大小为20。 - 第N+2行:数字M,代表下面有M行的函数直接调用关系。
- 接下来的M行,每行描述一个函数调用关系。第一个字母表示调用者,后面的字母表示被调用者,按顺序调用。例如:
A B C
表示A先调用B,然后调用C。 - 最后一行:数字K,代表系统配置的最大栈空间。
输出格式:
输出包括三个信息,用空格隔开:
- 是否会出现栈溢出,用
true
或false
表示。 - 如果栈溢出,输出发生栈溢出的调用路径(只输出到触发溢出的函数)。如果没有栈溢出,输出最大栈空间消耗的调用路径。如果有多个路径具有相同的栈消耗,输出第一个。
- 如果栈溢出,输出触发溢出的栈空间大小。如果没有栈溢出,输出最大栈消耗。
示例2解释:
输入:
6
A 20
B 10
C 5
D 15
E 50
F 60
3
A B C
B D
C E F
100
输出:
false A-C-F 85
解释:
-
函数调用关系和栈大小:
- A调用B和C
- B调用D
- C调用E和F
- 函数栈大小:A=20, B=10, C=5, D=15, E=50, F=60
- 系统最大栈空间K=100
-
计算不同调用路径的栈空间消耗:
-
路径A -> B -> D:
- A=20, B=10, D=15
- 总栈空间:20 + 10 + 15 = 45
-
路径A -> C -> E:
- A=20, C=5, E=50
- 总栈空间:20 + 5 + 50 = 75
-
路径A -> C -> F:
- A=20, C=5, F=60
- 总栈空间:20 + 5 + 60 = 85
-
-
最大栈空间消