我有以下 C 代码片段,它们显然都会导致堆栈溢出错误:
a.c
int f(int i) {
f(i);
}
int main() {
f(1);
}
b.c
int f(int i) {
f(i+1);
}
int main() {
f(1);
}
运行两者并查看
coredumpsctl list
中生成的结果后,输出大小非常不同:
Tue 2024-02-20 15:38:28 +0330 420696 1000 1000 SIGSEGV present /tmp/a 204.2K
Tue 2024-02-20 15:38:30 +0330 420710 1000 1000 SIGSEGV present /tmp/b 899.7K
第二个程序的 (
b.c
) 核心转储大小是第一个程序的 4 倍以上。这对我来说很奇怪,因为两个程序没有任何明显的差异。有人可以解释这种行为吗?
我使用此命令来编译这两个文件:
$ gcc a.c -o a && gcc b.c -o b
我使用的
gcc
版本:
$ gcc --version
gcc (Debian 12.2.0-14) 12.2.0
还为
a.c
生成了程序集(使用 objdump -S
):
0000000000001129 <f>:
1129: 55 push %rbp
112a: 48 89 e5 mov %rsp,%rbp
112d: 48 83 ec 10 sub $0x10,%rsp
1131: 89 7d fc mov %edi,-0x4(%rbp)
1134: 8b 45 fc mov -0x4(%rbp),%eax
1137: 89 c7 mov %eax,%edi
1139: e8 eb ff ff ff call 1129 <f>
113e: 90 nop
113f: c9 leave
1140: c3 ret
0000000000001141 <main>:
1141: 55 push %rbp
1142: 48 89 e5 mov %rsp,%rbp
1145: bf 01 00 00 00 mov $0x1,%edi
114a: e8 da ff ff ff call 1129 <f>
114f: b8 00 00 00 00 mov $0x0,%eax
1154: 5d pop %rbp
1155: c3 ret
对于
b.c
:
0000000000001129 <f>:
1129: 55 push %rbp
112a: 48 89 e5 mov %rsp,%rbp
112d: 48 83 ec 10 sub $0x10,%rsp
1131: 89 7d fc mov %edi,-0x4(%rbp)
1134: 8b 45 fc mov -0x4(%rbp),%eax
1137: 83 c0 01 add $0x1,%eax
113a: 89 c7 mov %eax,%edi
113c: e8 e8 ff ff ff call 1129 <f>
1141: 90 nop
1142: c9 leave
1143: c3 ret
0000000000001144 <main>:
1144: 55 push %rbp
1145: 48 89 e5 mov %rsp,%rbp
1148: bf 01 00 00 00 mov $0x1,%edi
114d: e8 d7 ff ff ff call 1129 <f>
1152: b8 00 00 00 00 mov $0x0,%eax
1157: 5d pop %rbp
1158: c3 ret
核心转储大小的差异是由于编译器优化造成的。该问题是由于编译器优化两个程序中的递归函数调用的方式不同造成的。由于您的代码都表现出无限递归,这最终将导致堆栈溢出错误。
在
a.c
中,每个递归调用都与前一个相同,从而导致tail-recursive
模式,并且许多编译器能够通过将tail-recursive
函数转换为迭代循环来优化它们,这会导致更有效的堆栈使用和较小的核心转储大小。
而在
b.c
中,i
在进行递归调用之前被修改,这会阻止编译器将递归优化为 tail-recursive
模式,最终导致更大的堆栈使用和更大的核心转储大小。