为什么我的 C++ 程序在我忘记 return 语句时崩溃,而不仅仅是 returning 垃圾?
Why does my C++ program crash when I forget the return statement, rather than just returning garbage?
我最近开始使用 CLang 编译嵌入式 C++ ARM 程序。
在此之前,我使用 GCC 和 C,几乎专门用于嵌入式工作。
我注意到,当我有一个方法 return 是一个值时,我忘记了 return 语句,程序核心转储。除了 "msleep error -1" 之外,我的一个设备驱动程序没有打印任何错误。这是在 FreeBSD 上。
我希望忘记 return 语句只会导致从函数中 returned 垃圾,而不是核心转储。
编辑:我return是一个布尔值,不是指针或对象或任何复杂的东西。即使 return 值无关紧要,程序也会崩溃。
这是怎么回事?
例如:
bool MyClass::DummyFunc() {
<do some stuff and forget the return value>
}
其他地方:
if(pMyObj->DummyFunc()) {
print ("Hey, it's true!\n");
} else {
print ("Darn, it's false!\n");
}
无论 return 值如何,该代码都不应崩溃。
来自二手资源,因为我不想为 C++ 标准付费,它显然说:
Flowing off the end of a function is equivalent to a return with no
value; this results in undefined behavior in a value-returning
function.
这是 C++,当你做一些未定义的事情时,你应该预料到它会使你的计算机崩溃并吃掉你的衣服,除非实现另有说明。
I would expect that forgetting the return statement would just result in garbage being returned from the function, not a core dump.
What is going on?
你的期望错了,是怎么回事。
编译器——一个极其复杂的机器——可以自由地假设你的函数return是一个值,因为你保证它是。
那你违背了诺言。
这就是为什么我们不应该对实现细节做出假设,尤其是在编写行为未定义的程序时。您不能直接比较 C++ 代码和 "some data goes into this register and there is this call stack that does precisely this thing".
这有近乎无限的方法可以破坏,因为在将 C++ 代码转换为计算机可读的经过优化的程序的过程中,编译器会进行一系列极其复杂的优化,并且任何一个如果您违反标准规则,它们可能会被折成两半。尝试准确确定程序的任何特定 运行 上发生了什么需要以下知识:
- CPU 品牌、型号和版本
- 操作系统品牌、型号和版本
- 编译器品牌、型号和版本
- 所有编译器标志
- 10 年编译器源代码经验
- 时间机器在程序执行时检查计算机中每一位内存的状态。
实在是不值得
幸运的是,在某些情况下,我们可以在不背离抽象的情况下对这种情况进行额外的合理化:例如,考虑一下当您承诺 return std::string
但没有做到时会发生什么t,然后那个不存在的 std::string
超出了范围。正在随机调用析构函数,这不会顺利进行。
忘记 return 值会导致控制流到达函数末尾。 C 和 C++ 标准都描述了这种情况。请注意,main 是这种情况的一个例外,将单独描述。
Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior
in a value-returning function C++11 Draft pg 136
我对 clang 的经验是,编译器会将 "undefined behavior" 的实例视为 不会发生 并优化路径。这是合法的,因为行为是未定义的。通常 clang 会沿着省略的路径发出非法指令,这样如果 "impossible" 发生,代码就会崩溃,这很可能是你的情况。事实上,编译器然后可以确定调用 DummyFunc() 会导致未定义的行为,因此不会发生,而是开始优化调用主体。
gcc 远 "friendlier" 并尝试生成一些不错的东西,例如 returning 0.
请注意,两个编译器都是正确的,并且正在根据标准生成有效代码。
我最近开始使用 CLang 编译嵌入式 C++ ARM 程序。
在此之前,我使用 GCC 和 C,几乎专门用于嵌入式工作。
我注意到,当我有一个方法 return 是一个值时,我忘记了 return 语句,程序核心转储。除了 "msleep error -1" 之外,我的一个设备驱动程序没有打印任何错误。这是在 FreeBSD 上。
我希望忘记 return 语句只会导致从函数中 returned 垃圾,而不是核心转储。
编辑:我return是一个布尔值,不是指针或对象或任何复杂的东西。即使 return 值无关紧要,程序也会崩溃。
这是怎么回事?
例如:
bool MyClass::DummyFunc() {
<do some stuff and forget the return value>
}
其他地方:
if(pMyObj->DummyFunc()) {
print ("Hey, it's true!\n");
} else {
print ("Darn, it's false!\n");
}
无论 return 值如何,该代码都不应崩溃。
来自二手资源,因为我不想为 C++ 标准付费,它显然说:
Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.
这是 C++,当你做一些未定义的事情时,你应该预料到它会使你的计算机崩溃并吃掉你的衣服,除非实现另有说明。
I would expect that forgetting the return statement would just result in garbage being returned from the function, not a core dump.
What is going on?
你的期望错了,是怎么回事。
编译器——一个极其复杂的机器——可以自由地假设你的函数return是一个值,因为你保证它是。
那你违背了诺言。
这就是为什么我们不应该对实现细节做出假设,尤其是在编写行为未定义的程序时。您不能直接比较 C++ 代码和 "some data goes into this register and there is this call stack that does precisely this thing".
这有近乎无限的方法可以破坏,因为在将 C++ 代码转换为计算机可读的经过优化的程序的过程中,编译器会进行一系列极其复杂的优化,并且任何一个如果您违反标准规则,它们可能会被折成两半。尝试准确确定程序的任何特定 运行 上发生了什么需要以下知识:
- CPU 品牌、型号和版本
- 操作系统品牌、型号和版本
- 编译器品牌、型号和版本
- 所有编译器标志
- 10 年编译器源代码经验
- 时间机器在程序执行时检查计算机中每一位内存的状态。
实在是不值得
幸运的是,在某些情况下,我们可以在不背离抽象的情况下对这种情况进行额外的合理化:例如,考虑一下当您承诺 return std::string
但没有做到时会发生什么t,然后那个不存在的 std::string
超出了范围。正在随机调用析构函数,这不会顺利进行。
忘记 return 值会导致控制流到达函数末尾。 C 和 C++ 标准都描述了这种情况。请注意,main 是这种情况的一个例外,将单独描述。
Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function C++11 Draft pg 136
我对 clang 的经验是,编译器会将 "undefined behavior" 的实例视为 不会发生 并优化路径。这是合法的,因为行为是未定义的。通常 clang 会沿着省略的路径发出非法指令,这样如果 "impossible" 发生,代码就会崩溃,这很可能是你的情况。事实上,编译器然后可以确定调用 DummyFunc() 会导致未定义的行为,因此不会发生,而是开始优化调用主体。
gcc 远 "friendlier" 并尝试生成一些不错的东西,例如 returning 0.
请注意,两个编译器都是正确的,并且正在根据标准生成有效代码。