表达式 'i < 0' 始终为假

Expression 'i < 0' is always false

对于以下片段:

size_t i = 0;
std::wstring s;
s = (i < 0)   ? L"ABC" : L"DEF";
s = (i != -1) ? L"ABC" : L"DEF";

PVS-Studio 分析记录第一个条件 i < 0 的警告,正如预期的那样:

V547 Expression 'i < 0' is always false. Unsigned type value is never < 0. test_cpp_vs2017.cpp 19

例如,为什么 PVS 不针对第二个也是可疑的情况发出任何警告 i != -1 报告它始终为真?

因为那将是一个无用的、无效的警告。 size_t 是无符号类型,由于整数转换的工作方式(参见 [conv.integral]/2),-1 转换(此处隐式)为 size_t 等于SIZE_MAX.

考虑一下,这是 libstdc++ 中 std::string::npos 的实际定义:

static const size_type  npos = static_cast<size_type>(-1);

如果 PVS-Studio 警告 i != -1,是否还需要警告 i != std::string::npos

另一方面,无符号值永远不会小于 0,因为它是无符号的,所以 i < 0 可能不是程序员想要的,因此警告是有道理的。

这是由于两种情况下的隐式整数转换size_t 必须 是至少 16 位的无符号类型,并且在您的情况下它的大小足够 cf。 int 如果一个参数是 size_t 而另一个是 int,那么 int 参数将转换为 size_t

计算 i < 0 时,0 被转换为 size_t 类型。两个操作数都是 size_t 所以表达式总是 false.

计算i != -1时,-1也被转换为size_t。该值将为 std::numeric_limits<size_t>::max().

参考:http://en.cppreference.com/w/cpp/language/implicit_conversion

当一个值被转换为无符号时,如果该值不能用无符号类型表示,那么该值将被转换为一个值(或者更确切地说, 值) 可表示的,并且与原始值模可表示值的数量一致(这是最大可表示值 + 1 == 2n其中 n 是位数)。

因此没有什么可警告的,因为有些值的条件可能为假(只要我们仅单独分析该表达式。i 始终为 0,因此条件为总是正确的,但为了能够证明这一点,我们必须考虑到程序的整个执行过程。

-1 与 m - 1 模 m 全等,因此 -1 始终转换为最大可表示值。

有正确的重要答案,但我想做一些澄清。不幸的是,测试示例的格式不正确。我们可以这样写:

void F1()
{
  size_t i = 0;
  std::wstring s;
  s = (i < 0)   ? L"ABC" : L"DEF";
  s = (i != -1) ? L"ABC" : L"DEF";
}

在这种情况下,分析器将发出两个 V547 警告:

  • V547 表达式 'i < 0' 始终为假。无符号类型值从不 < 0。consoleapplication1.cpp 15
  • V547 表达式 'i != - 1' 总是 真的。 consoleapplication1.cpp16

(V519也会发生,但与问题无关。)

因此,第一个 V547 警告被打印出来,因为无符号变量不能小于零。变量的值是多少也无关紧要。 发出第二个警告是因为分析器的反应是将 0 分配给变量 i,并且该变量在任何地方都没有改变。

现在让我们再写一个测试例子,让分析器对变量的值一无所知i:

void F2(size_t i)
{
  std::wstring s;
  s = (i < 0)   ? L"ABC" : L"DEF";
  s = (i != -1) ? L"ABC" : L"DEF";
}

现在只有一个V547警告:

  • V547 表达式 'i < 0' 始终为假。无符号类型值永远不会 < 0。consoleapplication1.cpp 22

分析器无法说明 (i != -1) 条件。这是完全正常的,例如,它可以是与 npos 的比较,正如已经注意到的那样。

我写这篇文章是为了防止有人决定使用 PVS-Studio 测试源代码示例,将其排除在外。当他看到两个警告时,这个人会感到惊讶,尽管讨论的是只有一个。