【问题标题】:Trivial function gives unexpected return value平凡的函数给出了意想不到的返回值
【发布时间】:2021-12-13 20:28:36
【问题描述】:

我编写了一个函数,该函数接收双矩阵并向后查找零条目。如果找到一个,则将该条目的值更改为-2.0 并返回true。否则返回false

代码如下:

#include <iostream>
#include <vector>

bool remove1zero(std::vector<std::vector<double>> & matrix)
{
    size_t dim = matrix.size();
    for (size_t j = dim - 1; j >= 0; j--)
        for (size_t i = dim - 1; i >= 0; i--)
            if ((matrix[j])[i] == 0.0)
            {
                (matrix[j])[i] = -2.0;
                return true;
            }
    return false;
}

int main()
{
    std::vector<std::vector<double>> testMatrix(3);
    testMatrix[0] = std::vector<double> {-2.0, -2.0, 3.0};
    testMatrix[1] = std::vector<double> {-2.0, -1.0, 3.0};
    testMatrix[2] = std::vector<double> {2.0, 2.0, -1.0};
    std::cout << remove1zero(testMatrix);
}

由于该矩阵没有零条目,因此不应激活 if 条件,最终 remove1zero 应返回 false。然而,事实并非如此。我在我的机器上以及http://cpp.sh/ 上都试过了,输出是1/true。我将不胜感激任何关于为什么会发生这种情况的见解。

【问题讨论】:

  • 使用(无符号)size_t j = dim - 1;j &gt;= 0 始终为真。所以你有越界访问,而不是停止循环。
  • [OT]:(matrix[j])[i]不用括号,matrix[j][i]就够了。
  • @Jarod42 现在一切都说得通了!奇怪的是,当尝试递减当前值为0size_t 变量时没有任何抱怨。谢谢!!
  • @edgystyle unsigned 整数类型被定义为使用模运算工作,因此需要从 0 递减以返回最大值。由于这是可靠的,因此开发人员可以有意义地使用它,因此不一定是错误。当允许的事情完成时,抱怨是不切实际的。不过,对于 signed 整数类型,上溢和下溢是未定义行为,编译器可能发出警告,或者它可能在运行时崩溃或抱怨。
  • @Jarod42 gcc 确实对此发出警告,但您需要 -Wextra-Wtype-limitsgodbolt.org/z/ThMnGvPrE

标签: c++ boolean double


【解决方案1】:

如 cmets 中所述,由于 size_t无符号 类型,j &gt;= 0i &gt;= 0 比较将始终评估为“真”,并且,当任一索引达到零时,下一个值(在减少该零值之后)将环绕到 size_t 类型的最大值,导致未定义的行为(越界访问)。

解决这个问题的一个很好的“技巧”是使用“goes to”伪运算符--&gt;,它实际上是两个运算符的组合:What is the "-->" operator in C/C++?

您可以在您的for 循环中使用它,如下所述,将“迭代表达式”留空(因为在“条件表达式”中完成减量)并在“init”中的一个更高的索引处开始循环-statement”(因为该减量将在进入循环体之前应用)。

这是使用这种方法的函数版本(请注意,我在 x-- &gt; 0 表达式中包含了一个空格,以澄清实际上涉及两个单独的运算符):

bool remove1zero(std::vector<std::vector<double>>& matrix)
{
    size_t dim = matrix.size();
    for (size_t j = dim ; j-- > 0; )
        for (size_t i = dim ; i-- > 0; )
            if (matrix[j][i] == 0.0) {
                matrix[j][i] = -2.0;
                return true;
            }
    return false;
}

【讨论】:

    【解决方案2】:

    size_t 等于 unsigned long。当您在 for 循环中检查 ji 时,您无法获得负数。因此ji在0减1时会得到一个奇怪的值。解决方法是将size_t改为简单的long

    #include <iostream>
    #include <vector>
    
    bool remove1zero(std::vector<std::vector<double>> & matrix)
    {
        long dim = (long)matrix.size();
        for (long j = dim - 1; j >= 0; j--)
        {
            for (long i = dim - 1; i >= 0; i--)
                if (matrix[j][i] == 0.0)
                {
                    matrix[j][i] = -2.0;
                    return true;
                }
        }
        return false;
    }
    
    int main()
    {
        std::vector<std::vector<double>> testMatrix(3);
        testMatrix[0] = std::vector<double> {-2.0, -2.0, 3.0};
        testMatrix[1] = std::vector<double> {-2.0, -1.0, 3.0};
        testMatrix[2] = std::vector<double> {2.0, 2.0, -1.0};
        std::cout << remove1zero(testMatrix);
    }
    

    【讨论】:

      【解决方案3】:

      不要使用 size_t 作为循环的类型。它可以评估小于零,因为它是无符号的。只需将大小转换为整数并使用它。

      bool remove1zero(std::vector<std::vector<double>> & matrix)
      {
          int dim = (int)matrix.size();
          printf("matrix size=%d\n",dim);
          for (int j = dim - 1; j >= 0; j--)
              for (int i = dim - 1; i >= 0; i--)
              {
                  printf("%d, %d\n",i,j);
                  if ((matrix[j])[i] == 0.0)
                  {
                      (matrix[j])[i] = -2.0;
                      return true;
                  }
              }
          return false;
      }
      

      【讨论】:

        【解决方案4】:

        使用(无符号)size_t j = dim - 1;j &gt;= 0 始终为 true。所以你有越界访问,而不是停止循环。

        在 C++20 中,使用 std::ranges::reverse_view

        你可能会这样做:

        bool remove1zero(std::vector<std::vector<double>> & matrix)
        {
            for (auto& col : matrix | std::views::reverse)) {
                for (double& d : col | std::views::reverse) {
                    if (d == 0.0) {
                        d = -2.0;
                        return true;
                    }
                }
            }
            return false;
        }
        

        Demo

        【讨论】:

          猜你喜欢
          • 2014-06-22
          • 1970-01-01
          • 2011-11-29
          • 1970-01-01
          • 2014-05-24
          • 1970-01-01
          • 2017-10-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多