我知道C ++ 中的“未定义行为”几乎可以允许编译器执行其想要的任何操作。但是,由于我认为代码足够安全,因此发生了一次崩溃,这让我感到惊讶。
在这种情况下,真正的问题仅在使用特定编译器的特定平台上发生,并且仅在启用优化后才发生。
为了重现此问题并将其简化到最大程度,我尝试了几件事。下面是一个函数的提取物被称为Serialize,这将需要一个布尔参数,并复制字符串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,因为无论如何它都会评估为真或假。”
我已经设置了一个Compiler Explorer示例,该示例在反汇编中显示了问题,此处是完整的示例。注意:为了解决该问题,我发现有效的组合是通过将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);
}
该问题的出现是由于优化程序引起的:巧妙地推断出字符串“ true”和“ false”的长度仅相差1。因此,不是真正计算长度,而是使用bool本身的值,这应该从技术上讲,它可以是0或1,如下所示:
const size_t len = strlen(whichString); // original code
const size_t len = 5 - boolValue; // clang clever optimization
可以这么说,这是“聪明的”,我的问题是:C ++标准是否允许编译器假设布尔值只能以内部数字表示“ 0”或“ 1”并以这种方式使用?
还是这是一种实现定义的情况,在这种情况下,实现假设其所有布尔仅包含0或1,并且其他任何值都是未定义的行为范围?
饮歌长啸
互换的青春
相关分类