【问题标题】:Unable to explain the output for the following C++ snippet无法解释以下 C++ 代码段的输出
【发布时间】:2018-07-12 09:21:21
【问题描述】:
#include <bits/stdc++.h>
using namespace std;

int main() {
   int dp[5][6];
   memset(dp,0,sizeof(dp));
   dp[1][0]=0;
   for(int i=1;i<=9;i++){
      dp[1][i]=1;
   }
   cout<<dp[2][0]<<endl;
   cout<<dp[3][0]<<endl;
 }

上面的 sn-p 产生了一个意外的输出:

1
0

但是,当我将 dp 数组大小更改为 dp[100][100] 时, 我得到预期的输出:

0
0

我也尝试打印 2D 矩阵大小:

sizeof(dp)/sizeof(int)

我得到了正确的值:dp[5][6] 为 30,dp[100][100] 为 10000。 我使用 Linux 中的标准 g++ 编译器编译并执行了代码。 有人可以向我解释这个错误输出的原因吗?

【问题讨论】:

  • 尝试打印 sizeof(dp) ,我感觉它的实际值与你期望的不同。
  • 您的 for 循环编辑数组边界之外的元素。这是未定义的行为。它很可能开始将[2][0] 覆盖为[2][3],因为数组在内存中是连续的,但您永远无法确定。
  • 我想你有来自dp[1][6]=1;的UB。越界访问。
  • sizeof(dp) 给出的预期答案为 120 (4*5*6)。
  • @Yksisarvinen 这是真的。我正在大力这样做。我无法解释它如何更改范围内且未超出范围的元素?编译器是否将索引包装在 2D 矩阵中?

标签: c++ multidimensional-array output memset


【解决方案1】:

数组的维度是[5][6]。因此,当您尝试使用从19i 执行dp[1][i] = 1 时,在某些迭代中,dp[1][6] = 1 完成。由于 'row' 中的元素数量为 6,它执行与 dp[2][0] = 1 相同的操作,因为该二维数组的所有元素都按顺序存储。

所以基本上当您尝试访问dp[1][i] 时,它就像startOffset + 1 * rowSize + i。并且i 大于行大小。

【讨论】:

  • @FantasticMrFox 不,这不是 UB。数组的元素需要按顺序存储。想象一下这个数组将被声明为typedef int row_t[6]; row_t dp[5];ints里面的每个row_t对象都是按顺序存储的,每个row_t对象按顺序存储在dp-array中。 sizeof(row_t)6 * sizeof(int)sizeof(dp)5 * sizeof(row_t)。这些元素不可能不按顺序存储。
【解决方案2】:

附带说明,在 C++ 中使用 memset 对数组进行零初始化是一种不好的做法。最佳做法是使用aggregate initializer syntax

int dp[5][6] = {}; // Zero-initialize.

【讨论】:

    【解决方案3】:

    首先,这不是很好的代码风格,但是由于我不确定您要达到的目标,因此我不会建议如何改进它,而只是回答您的问题:解释输出。

    第一种情况

    int main() {
       int dp[5][6];
       memset(dp,0,sizeof(dp));
       dp[1][0]=0;
       for(int i=1;i<=9;i++){
          dp[1][i]=1;
       }
       cout<<dp[2][0]<<endl;
       cout<<dp[3][0]<<endl;
     }
    

    dp 的第二个索引范围从 05(因为您声明了 int dp[5][6] 并且 C++ 数组是从零开始的)。但是,然后将第二个索引(for 循环中的i)从1 循环到9。因此,索引6, 7, 8, 9 超出范围,通常这会使您的程序崩溃。

    它不会崩溃,因为您已经声明了一个二维数组,该数组被分配在一个连续的内存块中,因此dp[1][6] 溢出到二维数组的下一部分。即dp[1][6] 等价于dp[2][0]

    这就是为什么在第一种情况下,您会发现 dp[2][0] 等于 1

    但是,当您将声明更改为 dp[100][100] 时,6, 7, ... 是有效的第二个索引,因此它永远不会溢出,dp[2][0] 保持其初始值 0

    【讨论】:

    • 感谢您的反馈。当我收到这个错误时,我正在为一个问题编写一个 dp 解决方案。我的想法是越界访问元素会使程序崩溃。
    • @pratyushchaudhary 从技术上讲,访问具有越界索引的普通数组会导致未定义行为。也就是说,该标准没有说明编译器/运行时应该做什么。 通常你会发现它只是崩溃,但不要依赖它。如果您想要“更友好”的错误处理,请使用像 std::array&lt;T&gt;std::vector&lt;T&gt; 这样的 STL 容器。这些都更加更安全,轻微的性能损失几乎总是值得的。
    【解决方案4】:

    首先要提到的是 dp[5][6] 是一个二维数组,其中有 5 行,每行有 6 列。您可以将其可视化为网格。但实际情况是每一行都是依次排列的。第一行可以容纳 0 到 5。现在,当您在循环中分配 dp[1][6]=1 时,这意味着 dp[2][0] 被分配 1,因为 dp[1][5] 是第一行的第 1 行和第 6 个元素是第 2 行的第一个元素。对于 dp[2][1]、dp[2][2] 和 dp[2][3],您将得到相同的答案。但会在 dp[2][4] 处得到 0。

    有一件事要提一下,它在不同的架构中可能会有所不同。同样,不同的编译器可以以不同的方式工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-06-11
      • 2021-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-29
      • 1970-01-01
      • 2016-05-08
      相关资源
      最近更新 更多