我有一个存储在数组中的树,我正在尝试找到一个特定的节点:
std::vector<Node> nodes = ...
const unsigned short sentinel = -1;
unsigned short index = 0;
for (Node* node = &nodes[index]; // root node
index != sentinel;
node = &nodes[index])
{
if (foo(*node)) {
index = node->left;
} else {
index = node->right;
}
}
换句话说,没什么特别的。但是,MSVC 2012失败,尝试访问超出范围的nodes[sentinel]
。事实证明,它首先计算&nodes[index]
,然后测试index
。 (调试模式,无优化)。
对我而言,这看起来像代码生成错误,但我至少在十年内没有看到过这样的错误。这是简单的未经优化的代码。当然,即使重新排列,在node
测试之前实际上并未使用index
,并且在x86上使用这样的越界指针并不是非常不安全,但是MSVC的{{1}正确地断言那个非法索引。
干净的构建并再次检查组件;它是可重复的。树也不是空的,总是有一个根节点。
我是否忽略了某些内容,或者这是一个严重的编译错误?
答案 0 :(得分:6)
如果您有这样的for循环:
for (init; cond; step) { body; }
然后这是表达式/语句的执行顺序:
换句话说,它是这个的同义词:
{
init;
while (cond) {
body;
step;
}
}
在您的情况下, body 可能会index
设置为sentinel
。然后,您希望 cond 执行并中断循环,但请注意,在每次 body 执行之后,步骤在 cond之前执行。这意味着node = &nodes[index]
确实会被执行,新值为index
,即sentinel
。所以VS正在产生应有的东西。
你的循环似乎与传统的for
循环完全不同;我认为将它变成一个明确的while
循环会更有意义。如果我正在对您的代码进行代码审查,我肯定会要求。
答案 1 :(得分:3)
您的代码重写为while循环就像
Node* node = &nodes[index]; // root node
while(index != sentinel)
{
{
if (foo(*node)) {
index = node->left;
} else {
index = node->right;
}
}
node = &nodes[index];
}
最后一行可能是对节点[-1]的访问。
我将你的循环重写为
unsigned short index = 0;
do
{
Node* node = &nodes[index];
if (foo(*node)) {
index = node->left;
} else {
index = node->right;
}
} while(index != sentinel);
答案 2 :(得分:2)
&#34;选择&#34;不是破碎的。 ; - )
了解循环的执行方式。
初始化Node* node = &nodes[index]
检查索引index != sentinel
。出口?
循环体。这会更改index
!
node = &nodes[index]
返回2.
在第3步index == -1
之后,您将在步骤4中获得超出范围的权限。
答案 3 :(得分:1)
在for (init; check; step) { body }
表达式中,顺序为init
,check
,然后不断重复周期body
,step
,{{1所以check
在检查之前发生。
但是,你的循环在这里很奇怪,因为你不需要需要 step
参与体外活动!
您可以轻松地将其重写为:
node
这不仅更短,而且涉及变量的范围更紧密:)
答案 4 :(得分:-1)
可能有一个错误(如果作者不想要这种行为)与sentinel:unsigned short不能为-1; 它应该简短......或签名