无法解释以下 C++ 代码段的输出
Posted
技术标签:
【中文标题】无法解释以下 C++ 代码段的输出【英文标题】:Unable to explain the output for the following C++ snippet 【发布时间】: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 矩阵中?
【参考方案1】:
数组的维度是[5][6]
。因此,当您尝试使用从1
到9
的i
执行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];
。 int
s里面的每个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
的第二个索引范围从 0
到 5
(因为您声明了 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<T>
或 std::vector<T>
这样的 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。
有一件事要提一下,它在不同的架构中可能会有所不同。同样,不同的编译器可以以不同的方式工作。
【讨论】:
以上是关于无法解释以下 C++ 代码段的输出的主要内容,如果未能解决你的问题,请参考以下文章