平凡的函数给出了意想不到的 return 值

Trivial function gives unexpected return value

我编写了一个函数,它接收一个双精度矩阵并向后查找零条目。如果找到一个,它会将条目的值更改为 -2.0 和 returns true。否则它 returns 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 应该 return false。然而,事实并非如此。我已经在我的机器上和 http://cpp.sh/ 中试过了,输出是 1/true。我将不胜感激任何关于为什么会发生这种情况的见解。

如评论中所述,由于 size_tunsigned 类型,因此 j >= 0i >= 0 比较将 always 评估为“真”,当任一索引达到零时,下一个值(在递减该零值之后)将回绕到 size_t 类型的最大值,导致未定义的行为(越界访问)。

一个很好的 'trick' 解决这个问题的方法是使用“转到”伪运算符 -->,它实际上是两个运算符的组合:What is the "-->" operator in C/C++?.

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

这是您使用此方法的函数版本(请注意,我在 x-- > 0 表达式中包含了一个 space,以澄清实际上涉及两个独立的运算符):

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;
}

不要使用 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;
}

size_t 等于 unsigned long。当你在for循环中检查ji时,你不能得到一个负数。因此,当您从 0 中减去 1 时,ji 会得到一个奇怪的值。解决方案是将 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);
}

对于(无符号)size_t j = dim - 1;j >= 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