我知道c++中的“未定义行为”几乎可以让编译器做任何它想做的事情。然而,当我以为代码足够安全时,我却遇到了意外的崩溃。
在这种情况下,真正的问题只发生在使用特定编译器的特定平台上,而且只有启用了优化。
为了重现这个问题并最大限度地简化它,我尝试了几种方法。下面是一个名为Serialize的函数的摘录,它将接受bool形参,并将字符串true或false复制到现有的目标缓冲区。
如果bool形参是一个未初始化的值,这个函数是否会在代码复查中,实际上没有办法判断它是否会崩溃?
// Zero-filled global buffer of 16 characters
char destBuffer[16];
void Serialize(bool boolValue) {
// Determine which string to print based on boolValue
const char* whichString = boolValue ? "true" : "false";
// Compute the length of the string we selected
const size_t len = strlen(whichString);
// Copy string into destination buffer, which is zero-filled (thus already null-terminated)
memcpy(destBuffer, whichString, len);
}
如果这段代码使用clang 5.0.0 +优化执行,它将/可能崩溃。
期望的三元运算符boolValue ?"true": "false"对我来说看起来足够安全,我假设," boolValue中的垃圾值是什么并不重要,因为它无论如何都会计算为true或false。"
我已经设置了一个编译器资源管理器的例子,显示了在拆卸的问题,这里是完整的例子。注意:为了重现这个问题,我发现使用Clang 5.0.0与-O2优化的组合是有效的。
#include <iostream>
#include <cstring>
// Simple struct, with an empty constructor that doesn't initialize anything
struct FStruct {
bool uninitializedBool;
__attribute__ ((noinline)) // Note: the constructor must be declared noinline to trigger the problem
FStruct() {};
};
char destBuffer[16];
// Small utility function that allocates and returns a string "true" or "false" depending on the value of the parameter
void Serialize(bool boolValue) {
// Determine which string to print depending if 'boolValue' is evaluated as true or false
const char* whichString = boolValue ? "true" : "false";
// Compute the length of the string we selected
size_t len = strlen(whichString);
memcpy(destBuffer, whichString, len);
}
int main()
{
// Locally construct an instance of our struct here on the stack. The bool member uninitializedBool is uninitialized.
FStruct structInstance;
// Output "true" or "false" to stdout
Serialize(structInstance.uninitializedBool);
return 0;
}
问题是由优化器引起的:它很聪明地推断出字符串“true”和“false”的长度只差1。因此,它不是真正计算长度,而是使用bool本身的值,从技术上讲,它应该是0或1,并如下所示:
const size_t len = strlen(whichString); // original code
const size_t len = 5 - boolValue; // clang clever optimization
虽然这很“聪明”,但可以这么说,我的问题是:c++标准是否允许编译器假设bool类型只能有“0”或“1”的内部数字表示,并以这样的方式使用它?
或者这是一种实现定义的情况,在这种情况下,实现假设它的所有bool只包含0或1,任何其他值都是未定义的行为领域?