我知道C ++ 中的“未定义行为”几乎可以允许编译器执行其想要的任何操作。但是,由于我认为代码足够安全,因此发生了一次崩溃,这让我感到惊讶。在这种情况下,真正的问题仅在使用特定编译器的特定平台上发生,并且仅在启用优化后才发生。为了重现此问题并将其简化到最大程度,我尝试了几件事。下面是一个函数的提取物被称为Serialize,这将需要一个布尔参数,并复制字符串true或false到现有的目标缓冲区。此功能是否在代码审查中,如果bool参数是未初始化的值,实际上没有办法告诉它崩溃吗?// Zero-filled global buffer of 16 characterschar 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 anythingstruct 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 parametervoid 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 codeconst size_t len = 5 - boolValue; // clang clever optimization可以这么说,这是“聪明的”,我的问题是:C ++标准是否允许编译器假设布尔值只能以内部数字表示“ 0”或“ 1”并以这种方式使用?还是这是一种实现定义的情况,在这种情况下,实现假设其所有布尔仅包含0或1,并且其他任何值都是未定义的行为范围?
3 回答
饮歌长啸
TA贡献1951条经验 获得超3个赞
允许编译器假定作为参数传递的布尔值是有效的布尔值(即已初始化或转换为true或的值false)。该true值不必与整数1相同-实际上,可以有trueand的各种表示形式false-但是参数必须是这两个值之一的某种有效表示形式,其中“有效表示形式”是实现-定义。
因此,如果您未能初始化a bool,或者通过其他类型的指针成功覆盖了它,则编译器的假设将是错误的,并且将导致未定义的行为。您被警告过:
50)以本国际标准描述为“未定义”的方式使用布尔值,例如检查未初始化的自动对象的值,可能会导致其行为既不正确也不错误。(第6.9.1节“基本类型”第6段的脚注)
互换的青春
TA贡献1797条经验 获得超6个赞
该函数本身是正确的,但是在您的测试程序中,调用该函数的语句通过使用未初始化的变量的值导致未定义的行为。
该错误位于调用函数中,可以通过对调用函数进行代码审查或静态分析来检测到。使用您的编译器浏览器链接,gcc 8.2编译器确实可以检测到该错误。(也许您可以针对clang提交一个错误报告,指出它没有发现问题)。
未定义的行为意味着任何事情都会发生,包括程序在触发未定义的行为的事件后崩溃了几行。
注意 答案为“未定义的行为会导致_____吗?” 始终为“是”。从字面上看,这就是未定义行为的定义。
- 3 回答
- 0 关注
- 520 浏览
添加回答
举报
0/150
提交
取消