如何在C++中使用(computed)goto和longjmp?

12

我通常不编写C++,但是我的一个奇怪的计算机科学朋友看腻了我精彩的FORTRAN程序并挑战我重新用C++编写其中一个,因为他喜欢我的C++代码。 (我们在这里打赌钱。)确切的条款是它需要在现代C++编译器中可编译。也许他讨厌conio.h - 我不知道。

现在我意识到有完全好的编写C ++ 的方式,但是我正在努力使我的C ++ 版本尽可能地类似FORTRAN。对于额外的加分,这可能会在我转换代码时节省一些时间和精力。

那么!这带给我以下相关的查询:

关于goto:

  1. 如何使用goto?
  2. C++中的goto限制是什么?
  3. 有关作用域的任何问题?(我将尽可能全局范围,但你永远不知道。)
  4. 如果我使用GCC扩展到void指针数组中进行跳转,是否有任何新的关于未定义行为等方面的问题?


关于longjmp:

  1. 如何安全地使用longjmp?
  2. C++中对longjmp的限制是什么?
  3. 它对范围有何影响?
  4. 是否有任何特定时刻看起来longjmp应该安全,但实际上不安全,我应该注意的地方?
  5. 如何使用longjmp模拟计算goto?
  6. 如果我的程序中只有一个函数,那么使用longjmp相比使用goto是否有任何实际好处?

现在我主要关心的是使计算goto正常工作。看起来我可能会使用longjmp使其工作,因为void指针数组不是C++标准的一部分,而是GCC特定的扩展。


7
等待一大堆非答案的谴责使用goto语句的言论如雨后春笋般出现... 3... 2... 1... - Justin ᚅᚔᚈᚄᚒᚔ
17
感谢你尝试证明这一点,确切地说,“真正的程序员可以用任何语言编写FORTRAN” :) - user395760
5
如果你写的是“类FORTRAN”的C++代码,那么你不是在写C++。如果我参加了这个赌局,那么你很难说服我相信你已经尽到了自己的责任。 - wilhelmtell
4
很容易看出这里在问什么。这个问题不含糊、模糊、不完整、过于广泛或修辞性,并且可以在当前形式下合理地回答。请在投票关闭时不要随意选择一个原因。 - Troubadour
6
我投票支持将其关闭,原因是在C++中故意编写复杂的FORTRAN代码并征求“goto语句不好”的评论和争论对任何人都没有帮助。 - AJG85
显示剩余15条评论
7个回答

7

我愿意承担风险并接受负面评价。

我认为,如果你使用goto和longjmp,实际上相当于将Fortran编写成C ++,这样你的朋友可能会发现它更难阅读,甚至可能更难理解。 C ++语言与Fortran非常不同,我认为你不应该尝试直接从Fortran转换到C ++。这只会使C ++更难维护,你最好还是保留现有的代码库。

goto:您需要设置一个标记(my_label:),然后使用goto命令goto my_label;,这将导致程序流执行跳转到goto语句后的语句。您无法跳过变量的初始化或在函数之间跳转。您不能创建goto目标的数组,但您可以创建对象或函数指针的数组以进行跳转。

longjmp:如果您只有一个函数,则没有理由优先选择longjmp而不是goto。但是,如果您只有一个函数,那么您实际上并没有编写C ++,您最好长期维护Fortran。


4
你的目标需要修订。 - AJG85
3
我不明白为什么我的目标会成为你的事情。这个问题仍然是合理的。 - Code Maker
2
@代码制作者:我认为这更适合在喝啤酒的酒吧里讨论,而不是在在线问答论坛上。没有其他人会从答案中受益,刻意编写复杂和混乱的代码也没有任何实际用途,老实说,你更有可能引发goto指令的追捕,而不是得到任何真正的答案。 - AJG85
1
@AJG85,“没有其他人会从答案中受益” 真是扯淡。我正在询问关于goto和longjmp的一般性问题。任何需要复习这些内容并关注其使用细节的人都可以从这个问题中受益。 - Code Maker
2
@CodeMaker,你正在询问关于gotp和longjmp的一般性问题,这些问题可以通过阅读文档轻松回答。如果“常规参考”关闭原因已经实施,我会投票关闭。对于你的问题,*理想的答案基本上是文档的复述。 - R. Martinho Fernandes
显示剩余5条评论

4

你可能会因为使用 goto 而受到很多批评。通常我会跟着大家一起抨击,但是在这种情况下,它听起来更像是一个编码高尔夫游戏。所以给你一个例子。

使用 goto 将指令指针移动到代码中的“标签”,这是一个 C++ 标识符后跟一个冒号。这里有一个简单的工作程序示例:

#include <iostream>
#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
    int i = 0;
step:
    cout << "i = " << i;
    ++i;
    if( i < 10 )
        goto step;

}

在这种情况下,step: 是标签。
有关上下文的问题。
  • 您只能在当前函数中跳转到标签。
  • 如果您的goto跳过变量的初始化,则可能会引发未定义行为(代码可以编译,但您无法确定它实际会执行什么)。
  • 您不能进入try块或catch处理程序。但是,您可以从try块中goto out

如果满足其他要求,则可以使用指针等进行“can goto”。 如果所讨论的指针在调用现场和分支现场处于范围内,则没有问题。


谢谢。我觉得你可能误解了我的意思。我的意思是“你能把跳转标签作为指针、数组或向量来使用goto语句吗?” - Code Maker
1
OP说:“我通常不写C++”。也许可以将“UB”首字母缩写扩展为“未定义行为”,或者给出一个简要的定义。 - Robᵩ
@代码:你的意思是想在向量中存储跳转标签,是吗? - John Dibling
顺便问一下,@JohnDibling,你知道GCC的void指针标签是否会对这些限制产生影响吗? - Code Maker

3

8
我不会称MSDN为“参考书”,而是“一份”参考资料 :)(它说明:“描述了在Microsoft C++中实现的C ++编程语言。”) - Emile Vrijdags

3

计算的 goto --> switch

实际上,它们共享一个(但不是普遍的)基础实现作为跳转表。


有时候,如果一个开关只有几个选择,那么它很可能会被实现为一系列的 if / else if 语句。 - Dominik Grabiec
当然,编译器可以选择为计算跳转选择相同的实现方式。 - dmckee --- ex-moderator kitten
一个switch语句只构成了一个向前跳转。我可以通过在之前做一个computed_goto: switch (goto_variable) { ... }来近似计算goto,这样我就可以做goto_variable = 5; goto computed_goto;,但我希望在这里使用longjmp()。我记得在Java中也这样做过,只是使用了一个带标签的while(true)循环和continue指令,但不幸的是Java并不像Duff's设备友好。 - Code Maker

1

如果我理解原始问题,那么这个问题实际上是一个有趣的问题。重新表述问题(我认为是等价的问题):“如何在C中执行FORTRAN计算跳转?”

首先我们需要知道什么是计算跳转:这里是一个解释链接:http://h21007.www2.hp.com/portal/download/files/unprot/fortran/docs/lrm/lrm0124.htm

计算跳转的一个例子是:

    GO TO (12,24,36), INDEX

12、24 和 36 是语句编号。(C 语言标签可以作为等效项,但不是唯一的等效项。)

INDEX 是一个变量,但也可以是公式的结果。

以下是在 C 中执行相同操作的一种方式(但不是唯一的方式):

int SITU(int J, int K)
{
int raw_value = (J * 5) + K;

int index = (raw_value % 5) - 1;

return index;
}


int main(void)
{
int J = 5, K= 2;

// fortran computed goto statement: GO TO (320,330,340,350,360), SITU(J,K) + 1
switch (SITU(J,K) + 1)
{
case 0: // 320
    // code statement 320 goes here
    printf("Statement 320");
    break;
case 1: // 330
    // code statement 330 goes here
    printf("Statement 330");
    break;
case 2: // 340
    // code statement 340 goes here
    printf("Statement 340");
    break;
case 3: // 350
    // code statement 350 goes here
    printf("Statement 350");
    break;
case 4: // 360
    // code statement 360 goes here
    printf("Statement 360");
    break;
}

printf("\nPress Enter\n");
getchar();
return 0;
}

在这个例子中,我们可以看到,实现FORTRAN计算跳转并不需要使用C的goto语句!

关于你最后一句话:但是switch语句只是一种进阶(多目标)的goto形式。 - Marc van Leeuwen

0

有一个名为Labels as Values的GCC扩展,可以帮助您进行代码高尔夫,基本上提供了计算跳转。当然,您可以自动生成标签。您可能需要这样做,因为您无法知道每行生成多少字节的机器代码。


0

longjmp可以让你从信号处理程序中跳出来,这很好用 - 但它会增加一些混乱,因为它不会重置在setjmp行之前定义的函数内自动(基于堆栈)变量。


5
在很多情况下,这种低廉的代价是完全未定义的行为。 - Billy ONeal
从所有的声音来看,他试图编写复杂的代码来赢得赌注 - 我在真正的代码中永远不会使用goto或longjmp - 它们本身就很复杂。 :p - John Humphreys

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接