vscode 转到声明
![](https://i-blog.csdnimg.cn/blog_migrate/4d61909f12238359649cc3cdbfb87b05.png)
您可能不应该在代码中使用goto语句。
您不应该使用它们的主要原因是因为它们使某些人感到恐惧。 他们刚刚听说这很有害。 这就是他们所知道的。 生活中有很多这样的事情-也许大多数事情是这样的:您不会通过超越现状来赢得任何分数。
“发表声明被认为有害”是Edgser Dijkstra给ACM出版物的一封信。 它变得如此著名,“ [某物]被认为有害”已经成为计算机科学界的一个模因。
在大多数语言中,goto实际上并不有害。 一会儿,我会解决。 但是,在我建立goto并没有那么糟糕之前,我想指出这没有关系。 重点是挑起战斗。 当您通过超越常识而违反常规时,大多数人没有足够的知识去了解您是对的。 有时,有些人将完全缺乏曾经理解的认知能力。
在您的职业生涯中,重要的是要给人们他们想要和/或期望的东西。 对于许多了解最新趋势的人来说,他们想要和期望的是最新的Martin Fowler演讲的主题。 其他人希望并期望某些最佳实践的真正古老形式,例如大量的UML图。 关键是,人们对什么才是聪明形成了看法,他们完全希望所有聪明的人都那样做,而所有这样做的人都是聪明的。 如果您最终超过了某人对智能的个人定义,那么他们将不会理解您正在加注赌注,他们将不会理解您不同意的原因。 有说服力的论点是行不通的,因为您的精神不会超出他们从中获得灵感的任何来源。 无论您陈述的情况有多好,人们只会认为您因为愚蠢而没有得到您的陈述,而您却因为没有理解而变得愚蠢。
![](https://i-blog.csdnimg.cn/blog_migrate/be549c9f6ee7c9db8e497ede08e8e428.png)
迪克斯特拉(Dijkstra)于1968年写了他的原始文章。那是非常不同的时期。 像Dijkstra这样的计算机科学家仍在努力建立编程和编程语言的基本原理。 实际上,世界上所有计算机都是以个人的名字为基础的。 即使是简单的程序也要费力地编写,并且在浪费时间运行程序之前,还格外小心地尝试证明代码是正确的。 然后,Dijkstra拒绝了goto,因为他提倡诸如子过程,函数,循环等新生的程序结构,这些结构提供了比常用的if-then-goto代码模式更高级别的抽象。 他认为这个问题是二分法,但这始终是错误的二分法。 他没有想到结构化编程和goto可以并存,只要可以在某些关键方式上限制goto。 考虑以下摘录:
毫无限制地使用go to语句会立即导致结果,即很难找到有意义的坐标集来描述过程进度,这变得非常困难。 通常,人们也会考虑一些精心选择的变量的值,但这是不可能的,因为要理解这些值的含义与进步有关! 当然,使用go语句,仍可以通过计数器对程序自启动以来执行的操作数进行计数(即一种标准化的时钟)来唯一描述进度。 困难在于,尽管这样的坐标是唯一的,但却完全无济于事。 在这样的坐标系统中,定义所有这些进展点(例如n等于房间中的人数减去1)变得非常复杂。
— Edgser Dijkstra
Dijkstra的论点认为goto与结构化编程不兼容,这取决于goto搞砸“坐标”的想法。 坐标到底是什么? 看来他在谈论循环计数器之类的东西。 但这实际上没有任何意义。 请允许我翻译:用现代的术语来说,我们有一个坐标系统,称为调用堆栈。 Dijkstra所说的是goto语句会弄乱调用栈。 他本质上是在谈论goto用来跳入或跳出函数而无需重置堆栈框架。 作用域变量不会被清理,正确的变量也不会被分配。 但是实际上, 在现代编译器和语言中,这是不可能的 -编译器将goto标签的作用域与变量的作用域一样,并进行流分析以进行验证,例如goto不用于跳过变量声明。 这就是为什么这是错误的二分法的原因-我们有一些方法可以静态验证goto的使用方式是否会破坏程序的结构。
与Dijkstra的“被认为有害”的信相比,少穿衣服的计算机科学家Donald Knuth回答了一篇有关结构化编程的论文,其中包括示例,其中goto是获得预期结果的最有效方法之一。 相反,Knuth显示goto和结构化编程可以和谐共处。 而且,如上所述,现代编译器可以防止使用goto破坏“坐标”系统。
但是很多人都知道“发表声明被认为有害”的模因,很少有人知道克努斯的反驳。 很少有人了解其中之一。 当被问到为什么goto不好时,大多数人会说这使程序流程难以阅读。 (大多数人也认为那是Dijkstra的文章所说的,即使他们读了之后也是如此!)这是胡说。 1968年之前的典型goto代码流如下所示:
if ($WAITING) then goto INPUT
在2017年,相同的代码如下所示:
if (isWaiting){
GetUserInput();
}
无论哪种方式,代码流都将完全相同! 另一方面,当涉及现代的大多数情况时,goto的有效用法(阅读:仅限于单个函数),人们仍在进行“ goto”编程,他们只是在使用更冗长且成本更高的布尔值来进行编程。标志和if语句。 任何转到代码重新编写if语句将总是基本上更少可读有时稍微少高性能的。
考虑一下此代码,这是goto语句的合理用法,它会中断嵌套循环,并在满足条件时跳过某些代码。
/* find a marker in a matrix if one exists or place a default */
int row = 0;
int col = 0;
while (row < height) {
while (col < width) {
if (matrix[row][col] == MARKER_VAL) {
goto FOUND;
}
col++;
}
row++;
}
NOT_FOUND:
//place marker in approx. middle as default
row = height / 2;
col = width / 2;
FOUND:
compute_distances(matrix, row, col);
根据您使用的语言,有许多替代方法可以编写此代码。 它们中的任何一个都不会更具可读性,也不会更加安全。 特别是, if
使用if
语句,其可读性将大大降低。
Dijkstra原始论文的基本基础上下文-结构化编程-已经成功地编织到我们所有结构良好的第三代编程语言中。 但是在该框架内,goto并没有错,在大多数适当使用它的语言中。 检查您的语言手册。 如果将gotos直接编译为JUMP或BRANCH指令,则可能有问题。 在大多数语言中,情况并非如此。 如果您对goto语句执行不安全的操作(例如跳过变量声明或尝试跳转到其他函数中的标签),则C ++,C#和Java的编译器都会正确拒绝编译。 使用这些语言,goto可能引起的唯一问题是与任何控制流结构(例如if
都具有100%的共同点。 这里的主要例外是C,它早了几十年。 如果在嵌入式系统上使用Plain C,则可能既没有编译器也没有其他工具可以帮助您静态验证gotos的安全性,因此请保持距离。
事实是,goto是非理性禁忌的受害者。 围绕goto的公开讨论是如此严格地限制了这一点,如果没有说话者强迫性地模糊它的严重性,就无法讨论。 导致最难以理解的意大利面条式代码的现代编程结构是面向对象的继承,它很容易被滥用,比goto变得不易读一个数量级,并且具有自己独特的反模式分类,这会导致错误。 但是不存在继承的忌讳,相反,我们只是指出以这种方式滥用它是不好的编程,这是程序员的错,而不是结构本身的错。 那么,为什么相同的逻辑不适用于goto? 因为开始不合理; 这是禁忌。 后藤很好。
但是不要告诉别人。 他们会认为你很蠢。
附加物
正如我在文章的上半部分所预测的那样,许多读者都产生强烈的下意识的React,甚至拒绝接受娱乐,即使是暂时的或假设的关于goto的任何其他信念。 我对此无能为力,因为正如我所预料的那样—禁忌是很强的,而且大多数人在精神上无能为力。 但是我可以回答一个问题。 我有一个相当粗鲁的读者,坚持认为我错了,或者说Dijkstra的著名文章是关于goto
弄乱调用栈的。
绝对是 。 我本来应该链接迪克斯特拉的论文 。 这是一句话,在开始时,他清楚地确定了我们所知道的调用栈是他论文的中心前提。 我意识到他的语气很难看懂,但是如果你全神贯注,你就会明白的。 原文在链接中,但在此我将用省略号缩短摘录,以缩写他非常重复,过于防御的学术写作风格。 重点:
现在让我们考虑如何表征一个过程的进度。 […]如果程序文本是例如赋值语句的纯串联[…],则只需在程序文本中指向两个连续的动作描述之间的一点即可。 […]让我们将此类指向文本中适当位置的指针称为“文本索引”。
[…]
一旦我们将其纳入语言程序中,我们就必须承认,单一的文本索引已不再足够。 在文本索引指向过程主体内部的情况下,仅当我们还给出所引用的过程调用时,才能表征动态进度。 通过包含过程,我们可以通过一系列文本索引来表征过程的进度,该序列的长度等于过程调用的动态深度。
程序调用的深度? 它是调用栈。 调用栈实际上就是“转到认为有害的语句”的全部内容。 不可能理解迪杰斯特拉的文章,因此,如果不了解那么多,就无法理解他反对goto
论点。 而且,正如我在上面的文章中所论证的那样,Dijkstra的论点已过时,因为现代语言中的goto
明显地局限于调用堆栈的单个层中。
翻译自: https://hackernoon.com/go-to-statement-did-nothing-wrong-199bae7bda2e
vscode 转到声明