我发现存储在bool变量(btw Visual-C ++和clang ++)中的值存在差异,如果存储的值既不是true也不是false(如果它以某种方式被破坏),我和#39;我不确定它是否是Visual-C ++错误,或者它是否只是UB我应该忽略。
采取以下示例:
#include <cstdint>
#include <iostream>
#include <string>
#include <limits>
bool inLimits(bool const v)
{
return (static_cast<std::int32_t>(v) >= static_cast<std::int32_t>(std::numeric_limits<bool>::min()) && static_cast<std::int32_t>(v) <= static_cast<std::int32_t>(std::numeric_limits<bool>::max()));
}
int main()
{
bool b{ false };
bool const* const pb = reinterpret_cast<bool const*>(&b);
std::uint8_t * const pi = reinterpret_cast<std::uint8_t*>(&b);
std::cout << "b: " << b << " pb: " << (*pb) << " pi: " << std::to_string(*pi) << std::endl;
std::cout << "b is " << (inLimits(b) ? "" : "not ") << "in numeric limits for a bool" << std::endl;
*pi = 3; // Simulate a bad cast during boolean creation
bool const b2{ b };
bool const b3{ *pb };
std::cout << "b: " << b << " pb: " << (*pb) << " pi: " << std::to_string(*pi) << std::endl;
std::cout << "b2: " << b2 << " b3: " << b3 << std::endl;
std::cout << "b is " << (inLimits(b) ? "" : "not ") << "in numeric limits for a bool" << std::endl;
std::cout << "b2 is " << (inLimits(b2) ? "" : "not ") << "in numeric limits for a bool" << std::endl;
std::cout << "b3 is " << (inLimits(b3) ? "" : "not ") << "in numeric limits for a bool" << std::endl;
return 0;
}
这是Visual-C ++的输出
b: 0 pb: 0 pi: 0
b is in numeric limits for a bool
b: 3 pb: 3 pi: 3
b2: 3 b3: 3
b is not in numeric limits for a bool
b2 is not in numeric limits for a bool
b3 is not in numeric limits for a bool
这是clang ++的输出
b: 0 pb: 0 pi: 0
b is in numeric limits for a bool
b: 1 pb: 1 pi: 3
b2: 1 b3: 1
b is in numeric limits for a bool
b2 is in numeric limits for a bool
b3 is in numeric limits for a bool
在按值构造新的布尔值时,以及当它与流操作符一起使用时,看起来在clang ++中存在限制检查。
我应该忽略这一点,还是仅仅是Visual-C ++的错误? 谢谢!
修改 对于那些不了解样本目的的人来说,它只是一个展示模拟&#34;模拟&#34;内存损坏或代码的另一部分中的错误导致布尔值初始化为除了true或false之外的其他内容,无论bool的二进制表示形式如何。
(我想知道我是否必须使用断言保护我的代码在其他地方不正当使用,但仅当此行为不是UB时)
第二次修改 添加了numeric_limits代码。
答案 0 :(得分:3)
&#34;在存储值既不是真也不是假的情况下&#34;
为什么你认为是这样的? C ++不限制bool
的二进制表示。在某些编译器上,true
可以由00000011
表示,其他编译器可以选择将false
表示为00000011
。
但实际上,GCC和MSVC都没有对bool
值使用该位模式。这使得它确实是未定义的行为。 UB 永远不会成为编译器错误。错误是实现不能正常工作的地方,但UB特别意味着任何实际行为都是可以接受的。
答案 1 :(得分:2)
标准没有规定bool
的值表示是什么。编译器可以自由制定自己的规范。
您的证据表明VC ++要求true
仅表示为LSB集,而clang ++允许任何非零表示形式为true
。
对于VC ++,您的代码会在行bool const b2{ b };
上导致未定义的行为,特别是当它尝试从b
读取值时。在b
的存储中设置的位不对应b
的值,并且标准不定义在这种情况下发生的情况,因此它是未定义的行为。
当未定义的行为发生时,没有任何保证;程序的所有输出都没有意义。您无法根据此点之后(或实际上甚至在它之前)出现的输出语句推断出任何内容。
答案 2 :(得分:1)
由于我没有,真的,在C ++标准中找到有关指针到bool(或等效)的转换的信息(如果定义了这些的使用),我不愿意将此作为答案发布。但是,在第二个想法,我也可以发布它 - 它可能会被其他人详细阐述。
首先,C ++ 14标准将bool
定义为:
[basic.fundamental]
- bool类型的值为true或false。 [注意:没有signed,unsigned,short或long bool类型或值。 - 尾注] bool类型的值参与整体促销(4.5)
醇>
由于它参与了整体促销活动,因此为其定义了以下促销活动:
[conv.prom]
- bool类型的prvalue可以转换为int类型的prvalue,false变为0,true变为1。
醇>
而且,由于您使用std::ostream::operator<<
打印bool
,因此定义如下:
[ostream.inserters.arithmetic]
- 类num_get&lt;&gt;和num_put&lt;&gt;处理依赖于区域设置的数字格式和解析。
醇>
由于它使用num_put<>
作为实际输出,因此与bool
输出相关的片段定义为:
[facet.num.put.virtuals]
- if(str.flags()&amp; ios_base :: boolalpha)== 0返回do_put(out,str,fill,(int)val)
醇>
由于您未在示例中使用boolalpha
,因此应采用典型的整数提升规则(如上所述)。
此外,我仍然无法解释为什么std::to_string(*pi)
在*pi = 3
仍然打印3
两种情况下,但它可能在某种程度上与以下内容相关:
[expr.reinterpret.cast]
- [注意:reinterpret_cast执行的映射可能会或可能不会产生与原始值不同的表示.-尾注]
醇>
答案 3 :(得分:0)
不确定这是否有帮助,但g ++表现出与Visual-C ++相同的行为。
这是我得到的输出:
b: 0 pb: 0 pi: 0 b: 3 pb: 3 pi: 3 b2: 3 b3: 3
根据我的理解(我是关于c ++编译器的专家),reinterpret_cast
指示编译器将位集合视为新类型。因此,当您告诉编译器将布尔值的地址重新解释为8位整数时,它实际上也是将原始布尔值转换为8位整数(如果这是有意义的话)。 / p>
因此,如果我的解释是正确的(它不是),那么这可能是一个&#34;错误&#34;在clang ++中,而不是Visual或g ++。 reinterpret_cast
在编译器之间得不到很好的支持,因此在决定使用哪种行为时,这种行为绝对值得注意,如果出于某种原因这是必要的。
我刚刚意识到这并不能解释为什么b2和b3也是3(非布尔)。我不会想象将新的布尔值作为8位整数处理也是有意义的,无论reinterpret_cast
如何,所以请将其作为1个代表的人的价值。 :)