提问人:Dina 提问时间:5/31/2010 最后编辑:S.S. AnneDina 更新时间:10/13/2022 访问量:46160
什么是差一错误,如何解决?
What is an off-by-one error and how do I fix it?
答:
例如,当您打算执行 n 次循环并编写如下内容时,就会出现 off-by-one 错误:
for (int i = 1; i < n; ++i) { ... }
或:
for (int i = 0; i <= n; ++i) { ... }
在第一种情况下,循环将被执行 1 次,在第二种情况下,循环将执行 1 次。其他变化是可能的,但一般来说,由于循环变量的初始值或循环的结束条件中的错误,循环执行一次或一次太少。(n - 1)
(n + 1)
循环可以正确地写成:
for (int i = 0; i < n; ++i) { ... }
for 循环只是 while 循环的特例。在 while 循环中可能会犯同样类型的错误。
由于某些语言从零(例如 C)枚举向量,而其他语言从 1(例如 R)枚举向量,因此会出现常见的 off-by-1 混淆。因此,大小的向量在 C 中具有从 to 运行的成员,但在 R 中具有 from to 的成员。x
n
x[0]
x[n-1]
x[1]
x[n]
在编写循环增量的常用习语时,您还面临着一个偏离一个的挑战:
在 C 中:
i = (i+1)%n
在 R 中:
i <- (i-1)%%n + 1
假设您有以下代码,其中包含一个数组和一个循环:for
char exampleArray[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };
for(int i = 0; i <= 11; i++)
{
print(exampleArray[i])
}
看到这里的问题了吗?因为我计算了我的数组中有 11 个字符,所以我将循环设置为迭代 11 次。但是,在大多数语言中,数组从零开始,这意味着当我的代码打印出来时
exampleArray[11]
我将收到索引越界错误,因为示例中的数组在索引 11 处没有值。
在这种情况下,我可以通过简单地告诉我的循环少迭代一次来轻松解决这个问题。
调试此问题的最简单方法是打印出上限和下限,并查看哪个值生成索引越界错误,然后将值设置为比整个迭代中的值大 1 或小 1。
当然,这假设错误是由循环超过或小于数组边界 1 或小 1 产生的,还有其他情况可能会发生索引越界错误,但是,这是最常见的情况。越界索引将始终是指尝试访问数据不存在的数据,因为过去的边界不在数据边界内。
评论
偏离 1 错误是指您期望某物的值为 N,但实际上它最终为 N-1 或 N+1。例如,您期望程序执行 10 次操作,但最终执行了 9 或 11 次(一次太少或一次太多)。在编程中,这种情况在处理“for”循环时最常见。
此错误是由于误判而发生的,您没有意识到您用来跟踪计数的数字可能与您正在计数的事物数量不同。换句话说,您用来计数的数字可能与您正在计数的事物总数不同。没有什么可以强制要求这两件事是相同的。试着从 0 大声数到 10,你最终总共说了 11 个数字,但你说的最后一个数字是 10。
防止这个问题的一种方法是意识到我们的大脑有一种倾向(也许是认知偏差)会犯这个错误。牢记这一点可能有助于您识别和预防未来的情况。但我想,为了防止这个错误,你能做的最好的事情就是编写单元测试。这些测试将帮助你确保你的代码按预期运行。
当您尝试以字符串或数组的特定索引为目标(以切片或访问段)或循环访问它们的索引时,会出现一个错误(有时称为 OBOE)。
如果我们将 Javascript 视为一种示例语言,则索引从零开始,而不是从 1 开始,这意味着最后一个索引始终比项目的长度小 1。如果您尝试访问与长度相等的索引,程序可能会抛出一个
“索引超出范围”引用错误
或
打印。undefined
当您使用将索引范围作为参数的字符串或数组方法时,阅读该语言的文档并了解它们是否具有包容性(给定索引处的项是返回内容的一部分)会有所帮助。以下是一些偏离错误的示例:
let alphabet = "abcdefghijklmnopqrstuvwxyz";
let len = alphabet.length;
for (let i = 0; i <= len; i++) {
// loops one too many times at the end
console.log(alphabet[i]);
}
for (let j = 1; j < len; j++) {
// loops one too few times and misses the first character at index 0
console.log(alphabet[j]);
}
for (let k = 0; k < len; k++) {
// Goldilocks approves - this is just right
console.log(alphabet[k]);
}
一个简单的经验法则:
int i = 0; // if i is initiated as 0, always use a < or > in the condition
while (i < 10)
System.out.printf("%d ", i++);
int i = 1; // if i is initiated as 1, always use a <= or >= in the condition
while (i <= 10)
System.out.printf("%d ". i++);
评论