8. Bug 与 Error

news2024/12/26 21:41:29

        计算机程序中的缺陷通常被称为 bug。把它们想象成偶然爬进我们工作中的小东西,会让程序员感觉良好。当然,实际上是我们自己把它们放进去的。

        如果程序是思想的结晶,我们可以将错误大致分为思想混乱造成的错误和将思想转化为代码时引入错误造成的错误。前者通常比后者更难诊断和修复。

语言

        如果计算机对我们要做的事情有足够的了解,那么很多错误都可以由计算机自动指出来。但在这方面,JavaScript 的松散性是一个障碍。它对绑定和属性的概念非常模糊,以至于在实际运行程序之前很少能发现错误。即便如此,它还是允许你做一些明显毫无意义的事情,而不会抱怨,比如计算 true * “monkey”。

        JavaScript 也会抱怨一些事情。编写不符合语言语法的程序会立即引起计算机的抱怨。其他一些事情,比如调用一个非函数的东西,或者查找一个未定义值的属性,都会在程序尝试执行操作时报错。

        不过,通常情况下,你的无意义计算只会产生 NaN(不是数字)或未定义的值,而程序会继续愉快地运行,坚信自己在做有意义的事情。只有在假值经过多个函数后,错误才会显现出来。它可能根本不会引发错误,但会悄无声息地导致程序输出错误。查找此类问题的根源可能很困难。

在程序中查找错误的过程称为调试。

严格模式

        通过启用严格模式,JavaScript 可以变得更严格一些。这可以通过在文件或函数体的顶部加上 “use strict ”字符串来实现。下面是一个例子:

        类和模块(我们将在第 10 章讨论)中的代码自动严格。旧的非严格行为之所以仍然存在,只是因为一些旧代码可能依赖于它,而语言设计者努力避免破坏任何现有程序。

        通常情况下,当你忘记在绑定前面加上 let 时(如示例中的 counter),JavaScript 会悄悄创建一个全局绑定并使用它。而在严格模式下,则会报错。这非常有用。不过需要注意的是,如果绑定已经存在于作用域中的某个地方,那么这个方法就不起作用了。在这种情况下,循环仍会悄悄覆盖绑定的值。

        严格模式下的另一个变化是,在未作为方法调用的函数中,该绑定值为未定义。在严格模式之外进行此类调用时,this 指的是全局作用域对象,而全局作用域对象的属性就是全局绑定。因此,如果在严格模式下不小心错误地调用了方法或构造函数,JavaScript 在试图从中读取内容时就会产生错误,而不是愉快地写入全局作用域。

例如,下面的代码在调用构造函数时没有使用 new 关键字,这样它的 this 就不会指向一个新构造的对象:

对 Person 的假调用成功了,但返回了一个未定义的值,并创建了全局绑定名称。在严格模式下,结果则不同。

我们立即被告知出了问题。这很有帮助。

        幸运的是,使用类符号创建的构造函数如果在没有 new 的情况下被调用,总是会被抱怨,因此即使在非严格模式下,问题也不大。

        严格模式还做了一些事情。它不允许给一个函数提供多个同名参数,并完全删除了某些有问题的语言特性(如 with 语句,这种语句是错误的,本书不再讨论)。

总之,在程序顶端加上 “use strict ”很少有坏处,而且可能会帮助你发现问题。

类型

        有些语言希望在运行程序之前就知道所有绑定和表达式的类型。当某种类型的使用方式不一致时,它们会立即告诉你。JavaScript 只有在实际运行程序时才会考虑类型,而且即使在运行程序时也会经常尝试将值隐式地转换为它所期望的类型,因此帮不上什么忙。

        不过,类型还是为讨论程序提供了一个有用的框架。很多错误都是由于对进入函数或从函数中出来的值的类型感到困惑而造成的。如果把这些信息写下来,就不容易混淆了。

你可以在上一章的 findRoute 函数前添加类似下面的注释来描述它的类型:

用类型注释 JavaScript 程序有许多不同的约定。

        关于类型的一个问题是,它们需要引入自身的复杂性,才能描述出足够有用的代码。你认为从数组中随机返回一个元素的 randomPick 函数的类型是什么?你需要引入一个类型变量 T,它可以代表任何类型,这样你就可以给 randomPick 赋予 (T[]) → T(从 Ts 数组到 T 的函数)这样的类型。

        当程序的类型已知时,计算机就有可能为你检查这些类型,在程序运行前指出错误。有几种 JavaScript 方言可以在语言中添加类型并对其进行检查。最流行的一种叫做 TypeScript。如果你有兴趣为你的程序增加更多的严谨性,我建议你试一试。

在本书中,我们将继续使用原始、危险、未键入的 JavaScript 代码。

测试

        如果程序语言不能帮助我们发现错误,我们就只能通过运行程序,看看它是否做对了。

        一次又一次地手工操作是个非常糟糕的主意。这不仅令人讨厌,而且往往效果不佳,因为每次更改都要花费大量时间对所有内容进行详尽测试。

        计算机擅长重复性工作,而测试正是理想的重复性工作。自动测试是编写一个程序来测试另一个程序的过程。编写测试程序比手动测试要繁琐一些,但一旦完成,你就会获得一种超能力:只需几秒钟就能验证你的程序在你编写测试程序的所有情况下是否仍能正常运行。当你破坏了某些东西时,你会立即注意到,而不是在以后的某个时间随机遇到。

        测试通常采用小标签程序的形式,用于验证代码的某些方面。例如,toUpperCase 方法(标准方法,可能已经有人测试过了)的测试集可能是这样的:

        这样编写测试往往会产生相当重复、笨拙的代码。幸运的是,有一种软件可以帮助你构建和运行测试集合(测试套件),它提供了一种适合表达测试的语言(以函数和方法的形式),并在测试失败时输出信息。这些软件通常被称为测试运行程序。

        有些代码比其他代码更容易测试。一般来说,与代码交互的外部对象越多,设置测试上下文就越难。上一章中展示的编程风格使用自足的持久值而不是不断变化的对象,这种风格往往容易测试。

调试

        一旦你发现程序出了问题,因为它行为不端或产生错误,下一步就是找出问题所在。有时问题很明显。错误信息会指向程序中的某一行,如果查看错误描述和该行代码,通常就能发现问题所在。但并非总是如此。有时,引发问题的那行代码只是在其他地方产生的错误值以无效方式被使用的第一个地方。如果你已经解决了前面几章的练习,你可能已经经历过这种情况。

        下面的示例程序试图将一个整数转换成一个给定基数(十进制、二进制等)的字符串,方法是反复挑出最后一位数字,然后除以该数字,去掉该数字。但它目前产生的奇怪输出结果表明它存在一个错误。

        即使你已经看到了问题所在,也请暂时假装看不到。我们知道我们的程序出现了故障,我们想找出原因。这时,你必须克制住自己的冲动,不要随意修改代码,看看这样做是否能让程序变得更好。取而代之的是思考。分析正在发生的情况,并就可能发生的原因提出理论。然后进行更多的观察来验证这一理论--或者,如果你还没有理论,也可以进行更多的观察来帮助你提出一个理论。

        在程序中调用一些策略性的 console.log 调用,是获取关于程序正在做什么的额外信息的好方法。在本例中,我们希望 n 取值为 13、1 和 0。

        对。13 除以 10 不能得到整数。我们实际上需要的是 n = Math.floor(n/base),而不是 n /= base,这样数字就会正确地向右 “移动”。

        除了使用 console.log 来窥探程序的行为外,另一种方法是使用浏览器的调试器功能。浏览器具有在特定代码行上设置断点的功能。当程序执行到带有断点的行时,程序会暂停,你可以检查该点的绑定值。由于不同浏览器的调试器不尽相同,我就不详细介绍了,但请查看浏览器的开发工具或在网上搜索相关说明。

        另一种设置断点的方法是在程序中加入调试器语句(仅由该关键字组成)。如果浏览器的开发工具处于激活状态,程序在运行到该语句时就会暂停。

错误传播

        遗憾的是,并非所有问题程序员都能避免。如果你的程序以任何方式与外界通信,就有可能获得畸形输入、工作负荷过重或网络故障。

        如果你只是为自己编程,你可以忽略这些问题,直到它们发生。但如果你编写的程序将被其他人使用,你通常希望程序能做得更好,而不仅仅是崩溃。有时,正确的做法是接受不良输入并继续运行。在其他情况下,最好是向用户报告出错的原因,然后放弃。无论在哪种情况下,程序都必须积极采取措施来应对问题。

        假设有一个 promptNumber 函数,向用户询问一个数字并返回该数字。如果用户输入 “orange”,它应该返回什么?

一种方法是让它返回一个特殊值。这种值的常见选择是 null、undefined 或-1。

        现在,任何调用 promptNumber 的代码都必须检查是否读取了实际的数字,如果没有,则必须以某种方式恢复--可能是再次询问,也可能是填写默认值。或者,它可以再次向调用者返回一个特殊值,以表明它未能完成所要求的操作。

        在许多情况下,主要是当错误很常见并且调用者应该明确考虑到这些错误时,返回一个特殊值是一种很好的提示错误的方法。不过,它也有缺点。首先,如果函数已经可以返回所有可能的值,那该怎么办?在这样的函数中,你就必须像迭代器接口的下一个方法那样,将结果封装在一个对象中,以便区分成功与失败。

        返回特殊值的第二个问题是,它可能导致代码笨拙。如果一段代码调用 promptNumber 10 次,它就必须检查 10 次是否返回了空值。如果发现空值后的反应是简单地返回空值,那么函数的调用者就必须依次检查空值,以此类推。

异常

        当函数无法正常运行时,我们通常希望停止正在进行的工作,并立即跳转到知道如何处理问题的地方。这就是异常处理的作用。

        异常是一种机制,它可以让遇到问题的代码引发(或抛出)异常。异常可以是任何值。引发异常有点类似于函数的超级返回:异常不仅会跳出当前函数,还会跳出其调用者,一直跳到开始当前执行的第一个调用。这就是所谓的释放堆栈。你可能还记得第 3 章中提到的函数调用栈。异常会沿着堆栈向下缩放,丢弃它遇到的所有调用上下文。

        如果异常总是直接放大到堆栈底部,那么它们就没有什么用处了。它们只是提供了一种炸毁程序的新方法。异常的强大之处在于,你可以沿着堆栈设置 “障碍”,以便在异常向下放大时捕获它。一旦捕捉到异常,你就可以对它进行处理,解决问题,然后继续运行程序。

这里有一个例子:

        throw 关键字用于引发异常。捕获异常的方法是将一段代码封装在 try 代码块中,然后使用关键字 catch。当 try 代码块中的代码导致异常发生时,catch 代码块将被评估,括号中的名称将与异常值绑定。在 catch 代码块结束后,或者如果 try 代码块结束时没有问题,程序将在整个 try/catch 语句下继续执行。

        在本例中,我们使用 Error 构造函数创建了异常值。这是一个标准的 JavaScript 构造函数,用于创建一个带有消息属性的对象。Error 实例还会收集创建异常时存在的调用堆栈信息,即所谓的堆栈跟踪。这些信息存储在堆栈属性中,在调试问题时很有帮助:它会告诉我们问题发生在哪个函数中,哪些函数进行了失败调用。

        请注意,look 函数完全忽略了 promptDirection 可能出错的可能性。这就是异常的最大优势:只有在错误发生时和处理错误时才需要错误处理代码。中间的函数可以完全不用考虑。

嗯,几乎是...

清理例外情况

        异常的影响是另一种控制流。每一个可能导致异常的操作(几乎是每一次函数调用和属性访问)都可能导致控制权突然离开代码。

        这就意味着,当代码有多个副作用时,即使其 “常规 ”控制流看起来都会发生,异常也可能会阻止其中一些副作用的发生。

下面是一些非常糟糕的银行代码:

        转账函数将一笔钱从一个给定的账户转入另一个账户,在此过程中会询问另一个账户的名称。如果给定的账户名称无效,getAccount 会抛出异常。

        但是转账函数会先将钱从账户中取出,然后调用 getAccount,再将钱添加到另一个账户中。如果在这个过程中出现异常,钱就会消失。

        这段代码本可以写得更智能一些,例如在开始移动资金之前调用 getAccount。但类似的问题往往以更微妙的方式出现。即使是看起来不会抛出异常的函数,在特殊情况下或包含程序员错误时也可能会抛出异常。

        解决这一问题的方法之一是减少副作用。同样,计算新值而非更改现有数据的编程风格也有帮助。如果一段代码在创建新值的过程中停止运行,现有的数据结构不会受到破坏,这样就更容易恢复。

        由于这并不总是切实可行,所以 try 语句还有另一个特点:它们后面可能会跟一个 finally 代码块,以代替 catch 代码块或作为 catch 代码块的补充。finally 代码块表示 “无论发生什么,在尝试运行 try 代码块中的代码后,再运行此代码”。

        这个版本的函数会跟踪其运行进度,如果在离开时发现它在创建了不一致的程序状态时被终止,它就会修复所造成的损害。

        请注意,即使 finally 代码在 try 代码块中抛出异常时运行,它也不会干扰异常。在 finally 代码块运行后,堆栈会继续展开。

        编写即使异常在意想不到的地方出现也能可靠运行的程序是很困难的。很多人根本懒得去做,而且由于异常通常只在特殊情况下出现,因此问题可能很少发生,甚至从未被注意到。这究竟是好事还是坏事,取决于软件出现故障时会造成多大的损失。

选择性捕捉

        当一个异常没有被捕获而一直到达堆栈底部时,它就会被环境处理。这在不同环境中的含义各不相同。在浏览器中,错误描述通常会写入 JavaScript 控制台(可通过浏览器的 “工具 ”或 “开发人员 ”菜单访问)。我们将在第 20 章讨论的无浏览器 JavaScript 环境 Node.js 对数据损坏更为谨慎。当出现无法处理的异常时,它会中止整个进程。

        对于程序员的错误,让错误通过往往是最好的办法。未处理异常是程序崩溃的合理信号,在现代浏览器上,JavaScript 控制台会提供一些信息,告诉你问题发生时堆栈上有哪些函数调用。

对于在日常使用中预计会发生的问题,使用未处理异常导致程序崩溃是一种糟糕的策略。

        语言的无效使用,如引用不存在的绑定、查找 null 属性或调用非函数,也会导致异常的产生。这些异常也可以被捕获。

        当输入 catch body 时,我们只知道 try body 中的某些内容导致了异常。但我们不知道是什么引起了异常,也不知道是哪个异常。

        JavaScript(一个相当明显的疏忽)并不直接支持选择性捕获异常:要么全部捕获,要么一个都不捕获。这就很容易让人认为你捕获到的异常就是你在编写捕获块时考虑到的异常。

        但事实可能并非如此。其他一些假设可能被违反,或者你可能引入了一个导致异常的错误。下面是一个试图继续调用 promptDirection 直到得到有效答案的示例:

        for (;;) 结构是一种有意创建不会自行终止的循环的方法。只有在给出有效方向时,我们才会跳出循环。不幸的是,我们拼错了 promptDirection,这将导致 “未定义变量 ”错误。由于 catch 代码块完全忽略了异常值 (e),以为自己知道问题所在,因此错误地将绑定错误视为输入错误。这不仅会导致无限循环,还会 “掩盖 ”有关拼写错误绑定的有用错误信息。

        一般来说,除非是为了将异常 “路由 ”到其他地方--例如,通过网络告诉其他系统我们的程序崩溃了,否则不要一揽子捕获异常。即便如此,也要仔细考虑如何隐藏信息。

        我们要捕获一种特定的异常。我们可以通过在 catch 块中检查我们得到的异常是否是我们感兴趣的异常,如果不是,就重新抛出。但如何识别异常呢?

        我们可以将其信息属性与我们预期的错误信息进行比较。但这种编写代码的方式并不可靠--我们将使用供人类使用的信息(信息)来做出程序决策。一旦有人更改(或翻译)了信息,代码就会停止工作。

相反,让我们定义一种新的错误类型,并使用 instanceof 来识别它。

        新的错误类扩展了 Error。它没有定义自己的构造函数,这意味着它继承了 Error 的构造函数,后者需要一个字符串消息作为参数。事实上,它根本没有定义任何东西--该类是空的。InputError 对象的行为与 Error 对象类似,只是它们有一个不同的类,我们可以通过这个类来识别它们。

现在,循环可以更仔细地捕捉这些对象了。

        这将只捕获 InputError 实例,而让无关的异常通过。如果重新引入拼写错误,未定义的绑定错误将被正确报告。

断言

        断言是程序内部的检查,用于验证某些事情是否符合其应有的状态。它们不是用来处理正常运行中可能出现的情况,而是用来发现程序员的错误。

例如,如果 firstElement 被描述为一个永远不应该在空数组上调用的函数,我们可以这样写:

        现在,它不再默默地返回未定义(读取不存在的数组属性时会返回未定义),而是一旦你误用它,程序就会立即爆炸。这样,此类错误就不容易被忽视,也更容易在错误发生时找到原因。

        我不建议为每一种可能的错误输入编写断言。这将是一项很大的工作量,而且会导致代码非常嘈杂。你需要为容易犯的错误(或你发现自己会犯的错误)保留断言。

总结

        编程的一个重要部分就是查找、诊断和修复错误。如果有一个自动化测试套件或在程序中添加断言,问题就更容易被发现。

        对于由程序控制之外的因素导致的问题,通常应积极加以规划。有时,当问题可以在本地处理时,特殊返回值是跟踪问题的好方法。否则,异常可能是更好的选择。

        抛出异常会导致调用堆栈被释放,直到下一个外层 try/catch 块或堆栈底部。异常值将提供给捕获异常的 catch 块,该块应验证异常确实是预期的异常类型,然后对异常进行处理。为了帮助解决异常导致的控制流不可预测性问题,可以使用 finally 块来确保代码在块结束时始终运行。

练习
重试

        假设你有一个函数 primitiveMultiply,它在 20% 的情况下可以实现两个数字相乘,而在另外 80% 的情况下会引发一个类型为 MultiplicatorUnitFailure 的异常。编写一个函数来封装这个笨重的函数,并不断尝试直到调用成功,然后返回结果。

确保只处理你要处理的异常。

代码:

function reliableMultiply(a, b) {
  try {
    return primitiveMultiply(a, b);
  } catch (e) {
    if (e instanceof MultiplicatorUnitFailure) {
      console.log(e.message);
    }
  }
}
console.log(reliableMultiply(8,2));
上锁的箱子

请看下面这个(相当臆造的)对象:

这是一个带锁的盒子。盒子里有一个数组,但只有当盒子被解锁时才能访问它。

编写一个名为 withBoxUnlocked 的函数,以函数值作为参数,解锁盒子,运行函数,然后在返回之前确保盒子再次被锁定,无论参数函数是正常返回还是抛出异常。

代码:

function withBoxUnlocked(body) {
  box.unlock();
  try {
    body();
    console.log(box.content);
  } finally {
    box.lock();
  }
}

为了获得额外的分数,请确保如果在盒子已经解锁的情况下调用 withBoxUnlocked,盒子将保持解锁状态。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2183184.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

帝都程序猿十二时辰

前言 2019年度国产剧《长安十二时辰》火了,其口碑榜首、节奏紧凑、贴合原著、电影质感,都是这部剧的亮点。而最令人震撼的还是剧中对大唐盛世的还原,长安街坊的市容市貌、长安百姓的生活日常、长安风情的美轮美奂……而关于十二时辰的话题也接…

基础算法--双指针【概念+图解+题解+解释】

更多精彩内容..... 🎉❤️播主の主页✨😘 Stark、-CSDN博客 本文所在专栏: 数据结构与算法_Stark、的博客-CSDN博客 其它专栏: 学习专栏C语言_Stark、的博客-CSDN博客 项目实战C系列_Stark、的博客-CSDN博客​​​​​​ 座右铭&a…

【算法竞赛】堆

堆是一种树形结构,树的根是堆顶,堆顶始终保持为所有元素的最优值。 有最大堆和最小堆,最大堆的根节点是最大值,最小堆的根节点是最小值。 本节都以最小堆为例进行讲解。 堆一般用二叉树实现,称为二叉堆。 二叉堆的典型应用有堆排序和优先队列。 二叉堆的概念 二叉堆是一棵…

Mybatis-Plus新花样(二)

多种插件 Mybatis-plus给我们提供了各种各样的插件,方便我们快捷开发。 一. 插件配置 Configuration public class MybatisPlusConfig {Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor new MybatisPlusInter…

CMIS5.2_光模块切应用(Application Selection and Instantiation)

目录 重要概念 DP配置、应用声明、应用码的区别 Control Set Provision 和 Commission ApplyDPInit 和 ApplyImmediate 判断应用是否切换成功 以800G光模块的3个应用对应的DP配置举例 1*800G应用: 2*400G应用: 8*100G应用: 应用声明…

ControlGAN:Controllable Text-to-Image Generation

1 研究目的 当前的生成网络通常是不可控的,这意味着如果用户更改句子的某些单词,合成图像将与原始文本生成的合成图像显着不同;当给定的文本描述(例如颜色)发生变化时,鸟类的相应视觉属性被修改&#xff0c…

我博客网站又遭受CC攻击了,记录一下

2024.9.29凌晨4点攻击开始,攻击目标是我的图床tc.zeruns.tech和博客blog.zeruns.tech,图床用的cdn是多吉云融合CDN,流量被刷了20GB左右就触发峰值关闭CDN了,HTTPS请求次数被刷了1.1亿次,因为设置了QPS,实际…

Oracle bbed编译安装及配置

1. 什么是bbed ? Oracle Block Brower and EDitor Tool,是一个可以对oracle data block进行查看,编辑修改的内置工具。对于bbed,oracle本身是不提供支持的。 2. 如何编译bbed环境? 10g版本: 1) 编译bbed cd $ORACL…

【网络基础】网络常识快速入门知识清单,看这篇文章就够了

💐个人主页:初晴~ 在现在这个高度智能化的时代,网络几乎已经成为了空气一般无处不在。移动支付、网上购物、网络游戏、视频网站都离不开网络。你能想象如果没有网络的生活将会变成什么样吗🤔 然而如此对于如此重要的网络&#xf…

深度学习500问——Chapter17:模型压缩及移动端部署(2)

文章目录 17.4.6 低秩分解 17.4.7 总体压缩效果评价指标有哪些 17.4.8 几种轻量化网络结构对比 17.4.9 网络压缩未来研究方向有哪些 17.5 目前有哪些深度学习模型优化加速方法 17.5.1 模型优化加速方法 17.5.2 TensorRT加速原理 17.5.3 TensorRT如何优化重构模型 17.5.4 Tensor…

Unity中Mesh多种网格绘制模式使用方法参考

Unity中MeshFilter中的Mesh默认情况下使用MeshTopology.Trigangles类型绘制网格,就是通常的绘制三角形网格,实际上Mesh有五种绘制模式,对应MeshTopology的枚举,分别是 Triangles网格由三角形构成。Quads网格由四边形构成。Lines网…

多线程——认识线程(Thread)

目录 前言 一、第一个多线程程序 1.程序编写 2.介绍jconsole 二、创建线程 1.继承Thread类 ①重写run方法 ②重写run方法,使用匿名内部类 2.实现Runnable接口 ①重写run方法 ②重写run方法,使用匿名内部类 ③使用 lambda 表达式 三、多线程…

【吊打面试官系列-MySQL面试题】为表中得字段选择合适得数据类型

大家好,我是锋哥。今天分享关于【为表中得字段选择合适得数据类型】面试题,希望对大家有帮助; 为表中得字段选择合适得数据类型 字段类型优先级: 整形>date,time>enum,char>varchar>blob,text 优先考虑数字类型,其次是…

c++类与对象二

文章目录 C类与对象二类的实例化类对象内存大小计算this指针特性 C类与对象二 类的实例化 用类创建对象的过程,称之为类的实例化 类是对对象进行描述的,限定了类有哪些成员,定义一个类并没有开辟内存空间。例如需要学生填写的个人表格&…

js 如何获取当日零点整的时间戳

最近遇到个问题需要取当日的零点整的时间戳去存取日程 上代码: const timestr new Date().setHours(0, 0, 0, 0) console.log(timestr) 效果展示: Tips:除了 Java 以外的语言需要除以1000 具体视情况而定 Java、js的时间戳都是毫秒级的…

每日一练:腐烂的橘子

994. 腐烂的橘子 - 力扣(LeetCode) 题目要求: 在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一: 值 0 代表空单元格;值 1 代表新鲜橘子;值 2 代表腐烂的橘子。 每分钟&#xf…

leetcode35--搜索插入位置--二分查找刷题

搜索插入位置 一共会出现下面四种情况: 目标值在数组所有元素之前 目标值等于数组中某一个元素 目标值插入数组中的位置 目标值在数组所有元素之后 首先在二分查找的代码之前处理掉目标值在数组所有元素之前和之后的情况如果目标值在数组中的某个位置&#xff0c…

51单片机系列-按键检测原理

🌈个人主页:羽晨同学 💫个人格言:“成为自己未来的主人~” 独立按键是检测低电平的。 下面我们来看一张对应的电路原理图: 在这张图当中,P1,P2,P3内部都上拉了电阻,但是P0没有&am…

学习记录:js算法(四十九):二叉树的层序遍历

文章目录 二叉树的层序遍历网上思路队列循环 总结 二叉树的层序遍历 给你二叉树的根节点 root ,返回其节点值的层序遍历 。 (即逐层地,从左到右访问所有节点)。 图一: 示例 1:如图一 输入:roo…

【QT】亲测有效:“生成的目标文件包含了过多的段,超出了编译器或链接器允许的最大数量”错误的解决方案

在使用dlib开发人脸对齐功能时&#xff0c;出现了”生成的目标文件包含了过多的段&#xff0c;超出了编译器或链接器允许的最大数量的错误“。 主要功能代码如下&#xff1a; #include <QApplication> #include <QImage> #include <QDebug>#include <dlib…