差一错误(英语:Off-by-one error,缩写OBOE)是在计数时由于边界条件判断失误导致结果多了一或少了一的错误,通常指计算机编程中循环多了一次或者少了一次的程序错误,属于逻辑错误的一种。比如,程序员在循环中进行比较的时候,本该使用“小于等于”,但却使用了“小于”,或者是程序员没有考虑到一个序列是从0而不是1开始(许多程序语言的数组下标都是这样)。在数学领域,此错误也时有发生。

遍历数组

假设现在有一堆物品,按mn(含)依次编号。那么这堆物品的总数是多少?我们可能会直觉地认为有(n - m)个物品,但这就和正确答案差了一,犯了栅栏错误。正确答案应该是(nm + 1)个物品。

因此,计算机领域中,涉及范围的时候通常用半开区间来表示,从mn(含)的范围就表示成从mn + 1(不含),以避免栅栏错误。例如,一个迭代五次的循环可以写成0到5的半开区间:

for (i = 0; i < 5; i++)
{/* 循环体 */
}

循环体首次执行时,i等于0,接着i依次变为1、2、3、4。最后,i会变为5,因此i < 5为假(不成立),循环结束。然而,如果在比较中用的是<=(小于等于),循环体则会执行六次:i依次为0、1、2、3、4、5。同样,如果i的初值是1而不是0,那么循环体只会执行四次:i依次为1、2、3、4。这些情况都能产生差一错误。

还有一种情况就是该用while循环的地方却用了do-while循环(反之亦然)。Do-while的循环体至少会执行一次。

程序语言间的差异也会产生混淆。从0开始计数是程序语言最为常见的做法,而有些语言中却以1起始。Pascal语言中可以自定义数组下标的起始值。

栅栏错误

n个间隔的直栅栏有 n+1条栅栏柱。

栅栏错误(有时也称为电线杆错误或者灯柱错误)是差一错误的一种。如以下问题:

建造一条直栅栏(即不围圈),长30米、每条栅栏柱间相隔3米,需要多少条栅栏柱?

最容易想到的答案10是错的。这个栅栏有10个间隔,11条栅栏柱。

反过来,柱子数给定时,我们也很容易认为间隔数也是这么多。实际上间隔数要比柱子数少一个。

广义上这个问题可以这么表述:

n个电线杆之间有多少个间隔?

如果这一列电线杆不组成一个圈,那么正确答案是n-1,如果在此前提之下,首尾两端也计入间隔,那么正确答案是n+1,如果电线杆围成一圈,那么答案则是n。思考之前必须先明确问题的定义,一个情况下的答案可能并不适用于其他情况。栅栏错误往往就来源于在数物体还是数间隔的选择上出了差错。

除了计算长度,栅栏错误还会发生在其他单位中。例如,时间金字塔是一个每10年放置1块石块,总共要放置120块的公共艺术作品,完成这个作品需要花费1190年,而不是1200年。早期的栅栏错误也与时间有关,儒略历最开始计算闰年的方式不正确,导致每三年会有一次闰年(正常情况应该是每四年,即每三年)。

大数的差一错误一般都不会引起大问题。正是在小数字上,尤其是精确度要求很高的时候,差一错误可能造成灾难。如果负责计算的人员“重蹈覆辙”,差一错误甚至可以累积。

安全隐患

不当使用C标准库中的strncat()函数常常会导致差一错误和安全问题。程序员经常认为strncat()在写入字符串结束符时不会超过最大长度。事实上strncat()会在指定的最大长度之后一字节的位置写入字符串结束符。如下代码:

void foo (char *s)
{char buf[15];memset(buf, 0, sizeof(buf));strncat(buf, s, sizeof(buf)); // 最后一个参数应为 sizeof(buf)-1
}

差一错误之所以经常在使用C标准库的时候出现,是因为C标准库在需不需要减去一字节这个问题上标准不统一。fgets()strncpy()这些函数在写入的时候不会超过最大长度(fgets()会自行把长度减一,只取回(长度 - 1)字节),而像strncat()这些函数则会越过最大长度。所以程序员必须牢记哪些函数需要减去一。

在某些系统上(小端序架构),差一错误可导致帧指针的最低字节被覆盖,从而使攻击者能够劫持调用例程的局部变量,给漏洞攻击敞开大门。

要避免这类问题,可以使用这些函数的其他改进版本,如strlcat()strlcpy(),这些改进版在写入时会考虑缓冲区能容纳的大小,因此更加“安全”。(上面代码的相应部分改成strlcat(buf, s, sizeof(buf))就能解决问题)

总结:

差一错误 和 无边界字符串复制产生的后果类似,都会对数组产生越界写操作,这个越界写操作产生严重的内存溢出问题,而这个内存溢出问题在编编译的时候不会报错,甚至在运行时也不会出现问题,只是偶尔出现,这就给程序的不稳定性带来威胁。

void main(void)

{

char source[]="My name is Benjamin.Franklin";

char dest[10];

memset(dest,0,sizeof(dest));

char *result = strcpy(dest,source);

cout<<result<<endl;

}

输出:My name is Benjamin.Franklin

呵呵,没有出错而且居然输出全部字符串。这就是无边界字符串复制。和差一错误产生相同数组越界写操作。

C语言陷阱之差一错误相关推荐

  1. 面试中你必须要知道的语言陷阱

       如:"你经历太单纯,而我们需要的是社会经验丰富的人","你性格过于内向,这恐怕与我们的职业不合适","我们需要名牌院校的毕业生,你并非毕业于名牌 ...

  2. c语言 去掉双引号_技术分享|浅谈C语言陷阱和缺陷

    良好的软件架构.清晰的代码结构.掌握硬件.深入理解C语言是防错的要点,人的思维和经验积累对软件可靠性有很大影响.C语言诡异且有种种陷阱和缺陷,需要程序员多年历练才能达到较为完善的地步.软件的质量是由程 ...

  3. C 语言陷阱和缺陷(一)

    CSDN话题挑战赛第2期 参赛话题:学习笔记 文章目录 前言 简介 第一部分:词法缺陷 1. 1 = 不是 == 1.2 多字符记号 1.3 多字符记号一些例外说明 1.4 字符串和字符 第二部分:句 ...

  4. 在c语言中3ap是不合法的变量,C语言陷阱一

    原标题:C语言陷阱一 1. while ( c= ' ' || c== 't' || c== 'n') c=getc(f); 程序员有时候会将=写成==,因为赋值运算符=的优先级要低于逻辑运算符||, ...

  5. 警惕求职面试中的语言陷阱

    面试极像一次相亲 美的Uclean滤水壶.应聘者希望找到一个 美的Uclean滤水壶能够了解自己优点的老 美的Uclean滤水壶板,用人单位则希望能 美的Uclean滤水壶当陌生的双方相见后, 美的U ...

  6. 面试中你必须要知道的语言陷阱(很好,怕再找不到了)

    :"你经历太单纯,而我们需要的是社会经验丰富的人","你性格过于内向,这恐怕与我们的职业不合适","我们需要名牌院校的毕业生,你并非毕业于名牌院校&q ...

  7. 【IT职场】面试中你必须要知道的语言陷阱

    面试中你必须要知道的语言陷阱 如:"你经历太单纯,而我们需要的是社会经验丰富的人", "你性格过于内向,这恐怕与我们的职业不合适", "我们需要名牌院 ...

  8. hitchhiker_Hitchhiker的现代Android开发指南:陷阱和隐密错误

    hitchhiker 没有哪个平台或语言是完美的,每个平台或语言都有其自身的问题,陷阱和隐秘的错误. 得益于Java的传统,Android通常通过庞大的博客,论坛和stackoverflow答案数据库 ...

  9. hitchhiker开发_Hitchhiker的现代Android开发指南:陷阱和隐密错误

    hitchhiker开发 没有一个平台或语言是完美的,每个平台或语言都有其自身的问题,陷阱和隐秘的错误. 得益于Java的传统,Android通常通过庞大的博客,论坛和stackoverflow答案数 ...

最新文章

  1. IDEA新建springboot项目发生错误
  2. HTML cellpadding与cellspacing属性
  3. 解决mysql 1032 主从错误
  4. 本地chrome调试服务器node
  5. Jade报错:Invalid indentation,you can use tabs or spaces but not both问题
  6. 2017年的第一周,你吸了多少雾霾?R语言告诉你(代码)
  7. python使用匿名函数计算长方形的面积
  8. UNICODE,GBK,UTF-8区别
  9. python爬取58同城二手房信息_动手写爬虫(2):爬取58同城二手物品信息
  10. IoTDB PMC 黄向东成功入选『2021 中国开源先锋 33 人之心尖上的开源人物』,我们记录了他和 IoTDB 的故事...
  11. “二清”以纳入一级、二级黑名单
  12. python按照号段生成手机号接收验证码_django 发送手机验证码的示例代码
  13. 用纯CSS写一个左右滑动的开关按钮
  14. Java面试宝典2017版
  15. 电动车、船等 机械结构DIY
  16. 余文乐结婚,杜蕾斯文案炸了!
  17. Python统计模型探索性数据分析(EDA)系统(单变量-双变量-相关性-缺失值)
  18. 孙宇晨:“这就像1932年的毕加索”
  19. java水费管理系统课程设计_java水电费管理系统
  20. C语言入门Part8--操作符篇

热门文章

  1. 二元一次不定方程的解法
  2. python五子棋代码_python实现五子棋小游戏
  3. Chrome 已称王,IE 今何在?
  4. 关于python进度条中的pbar.update(10)——from tqdm import tqdm
  5. 【超硬核】一文打尽 Redis 核心技术
  6. P1553 数字反转(升级版)
  7. 资料打印不用再跑文印店,网上打印省时省力
  8. 6 赫斯曼网管软件industrial hivision用户管理
  9. 金字塔压力-面积-位移图像识别 外接矩形绘制(wcy)
  10. 云图科技,长沙VR全景拍摄来了解下?