- 此问题在此处已有答案**:
Checking return value of a function without return statement(3个答案)
Undefined behavior of a C function with no return type [duplicate](3个答案)
23小时前关门了。
今天我很惊讶:
#include <stdlib.h> // for the 'exit' call
int foo() {
// return 0;
}
int main() {
int res = foo();
exit(res);
}
我知道忘记在foo
中返回期望的整数值是不好的;但是你会期望这段代码出现segfault吗?
下面是GCC7.5的情况:
(thanassis)$ g++ -O3 -Wall a.cc
a.cc: In function ‘int foo()’:
a.cc:5:5: warning: no return statement in function returning non-void [-Wreturn-type]
}
^
(thanassis)$ ./a.out
(thanassis)$ gdb ./a.out
GNU gdb (Ubuntu 10.2-0ubuntu1~18.04~2) 10.2
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...
(No debugging symbols found in ./a.out)
(gdb) run
Starting program: /home/thanassis/a.out
[Inferior 1 (process 24749) exited normally]
(gdb) quit
没问题,一切顺利.
是的,foo
忽略了设置返回值,这意味着ABI(EAX)为任务选择的寄存器将有垃圾。
现在看看GCC11的情况:
(thanassis)$ g++ -O3 -Wall ./a.cc
./a.cc: In function ‘int foo()’:
./a.cc:5:5: warning: no return statement in function returning non-void [-Wreturn-type]
5 | }
| ^
(thanassis)$ ./a.out
Segmentation fault (core dumped)
(thanassis)$ gdb ./a.out
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...
(No debugging symbols found in ./a.out)
(gdb) run
Starting program: /home/thanassis/a.out
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ddbfaa in __libc_start_main (main=0x555555555044 <main>, argc=-136462205, argv=0x7fffff7ff0b0, init=0x555555555140 <__libc_csu_init>,
fini=0x5555555551b0 <__libc_csu_fini>, rtld_fini=0x7fffffffe0e8, stack_end=0x7fffff7ff0a8) at ../csu/libc-start.c:141
141 ../csu/libc-start.c: No such file or directory.
(gdb) bt
#0 0x00007ffff7ddbfaa in __libc_start_main (main=0x555555555044 <main>, argc=-136462205, argv=0x7fffff7ff0b0, init=0x555555555140 <__libc_csu_init>,
fini=0x5555555551b0 <__libc_csu_fini>, rtld_fini=0x7fffffffe0e8, stack_end=0x7fffff7ff0a8) at ../csu/libc-start.c:141
#1 0x000055555555507e in _start ()
(gdb)
现在这个,我没想到。
首先,堆栈帧看起来很混乱-main
堆栈帧在哪里?
查看main
的objdump输出...
$ objdump -d -S ./a.out
...
Disassembly of section .text:
0000000000001040 <_Z3foov>:
#include <stdlib.h>
int foo() {
1040: f3 0f 1e fa endbr64
0000000000001044 <main>:
// return 0;
}
int main() {
1044: f3 0f 1e fa endbr64
1048: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
104f: 00
0000000000001050 <_start>:
1050: f3 0f 1e fa endbr64
1054: 31 ed xor %ebp,%ebp
1056: 49 89 d1 mov %rdx,%r9
1059: 5e pop %rsi
105a: 48 89 e2 mov %rsp,%rdx
105d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
...看起来GCC决定"合并"堆栈帧?!
这看起来像是一个编译器错误。注意,忽略旧的编译器,它也不会在优化级别-O0
和-O1
中出现-但在-O2
之后会出现。
再次说明:我知道这是不好的形式,而且我确实使用了-Wall
和-Wextra
--所以我在代码中修复了这个问题,但我想在这里分享一下,因为我从来没有想到一个返回int的函数不返回int来创建segfault(由于编译器创建的代码错过了堆栈帧)。
- 更新**:另请注意,使用
gcc
而不是g++
编译会创建正常代码。此问题仅在使用C++编译器编译代码时出现。
- 更新**:另请注意,使用
1条答案
按热度按时间ki0zmccv1#
这不仅仅是“糟糕的形式”,而是undefined behavior。
你的函数在声明返回值时失败了,而你试图使用那个返回值,这触发了未定义的行为,在
-O3
的情况下会导致崩溃。这在C standard的第6.9.1p12节中详细说明:
如果到达了终止函数的
}
,并且调用方使用了函数调用的值,则该行为未定义。所以回答你的问题,不是编译器的错误,你只是做了一些你不应该做的事情。