使用goto语句的替代方法

3

我对编写一个程序感到困惑,该程序从用户那里获取'/'字符并警告用户不要在分子中使用0。我已经构建了这个程序,但我希望有一种替代方法来使用goto语句。

# include <stdio.h>
int main()
{
    char o;
    int num1,num2;
    printf("Enter one operator like '+' '-' '/' '%%' '*' : ");
    scanf("%c",&o);
    printf("Enter 1st number: ");
    scanf("%d",&num1);
my: printf("Enter 2nd number: ");
    scanf("%d",&num2);

我希望删除以下两行代码,但是它应该实现与原来相同的结果。

    if(o=='/' && num2==0 ){
        printf("You can't divide by zero \n");
        goto my;
    }    
    switch(o) {
        case '+':
            printf("%d + %d = %d",num1, num2, num1+num2);
            break;
        case '-':
            printf("%d - %d = %d",num1, num2, num1-num2);
            break;
        case '*':
            printf("%d * %d = %d",num1, num2, num1*num2);
            break;
        case '/':
            printf("%d / %d = %d",num1, num2, num1/num2);
            break;
        case '%':
            printf("%d %% %d = %d",num1, num2, num1%num2);
            break;
        default:
            printf("Operator is not correct");
            break;
    }
    printf("\n\n");
    system ("pause");
}

1
你不应该删除代码,使答案看起来毫无意义。 - Gopi
请参阅Goto语句仍被认为是有害的,了解关于goto语句的优缺点的讨论。 - Jonathan Leffler
你应该测试 scanf 的返回值,以避免未定义的行为和在 EOF 上无限循环。 - chqrlie
请注意,0不是唯一会导致除法溢出的值:INT_MIN / -1将产生异常并使您的程序崩溃。尝试输入-2147483648-1 - chqrlie
你只测试了零除法,但是取模运算符“%”也有同样的问题。 - chqrlie
3个回答

8

在本质上,goto并没有问题!

大多数人都未能理解这一点,而只是简单地模仿几十年前一个选择不当的代码片段中所说的内容。Dijkstra自己后来对此感到遗憾,并表示“感觉人们正在从中制造出一种宗教信仰”。如果人们只尝试去了解使用gotobreakcontinue的原因,或者限制函数的退出点等规则背后的原因,那么他们将成为更好的艺术实践者。

Dijkstra实际上反对的是“不受约束地使用goto语句”,这一点我完全赞同。在计算环境中,大多数情况下没有其他循环结构,只有goto语句。这可能导致很多的意大利面式代码,非常难以理解。

我认为产业中的盲目教条主义比goto语句更加成为问题。goto语句的问题在于其被错误地使用,因为它会使代码变得更难以理解,当我听到这些声明时,我通常会问:

这段代码难以理解或维护,是因为它违反了“规则”吗?

如果您按照以下方式重新组织您的代码:

// ===================================================
// Get the second number, disallowing zero if dividing.
getSecondNum:
    printf ("Enter 2nd number: ");
    scanf ("%d", &num2);
    if ((o == '/') && (num2 == 0)) {
        printf ("You can't divide by zero \n");
        goto getSecondNum;
    }
// ===================================================

你会很难想出一个基于dowhile的解决方案比这个更可读或易懂。你也许可以达到同样的可读性,但你经常会发现,在零次或多次do和一次或多次while之间选择不当,会使代码变得不易读。

说实话,这是我在非使用goto的情况下能够得到的最接近相同代码的方法:
// ===================================================
// Get the second number, disallowing zero if dividing.
do {
    printf ("Enter 2nd number: ");
    scanf ("%d", &num2);
    if ((o == '/') && (num2 == 0)) {
        printf ("You can't divide by zero \n");
} while ((o == '/') && (num2 == 0));
// ===================================================

但这样会产生其他问题,例如代码重复。这也可以解决,但通常需要使用更多的变量或使用break,它与goto在这里有完全相同的问题(b)


因此,最重要的是,不要把你不理解的东西当作真理,无论它来自Dijkstra、Knuth、dkr还是你信仰的其他神明 :-)

思考为什么它可能被认为是真理,然后才能决定它是否适用于特定情况(a)

我经常在状态机、错误处理等地方使用goto,这实际上简化了本来更难理解的代码。


(a) 这包括来自网络上随机人士的建议,甚至包括那些名叫“paxdiablo”的人的建议 :-)


(b) 你可能需要考虑的一件事是,即使是除法,也允许用户输入零,然后在计算中进行处理,例如:

case '/':
    if (num2 == 0)
        printf ("%d / 0 is undefined, cannot divide by zero", num1);
    else
        printf ("%d / %d = %d", num1, num2, num1 / num2);
    break;

我个人可能更喜欢尽早捕捉的选项,但这个可能对您来说是一个可行的解决方案。


我也用浮点数写了相同的代码,但是浮点数不适合使用“==”符号。那我该怎么办呢?@paxdiablo - Zainab Nabeel
@Zainab,比较浮点数是一个完全不同的问题,在SO上已经被问了很多次。基本上,你要检查是否“足够接近”,而不是相等,例如如果差异小于平均值的0.00001%,这取决于各种各样的因素。我建议在SO上搜索已经提出的关于这个主题的问题。 - paxdiablo
我已经搜索了关于浮点数的相关内容,并大致理解了其中的要点。现在,在整数变量中使用do while循环,如果我在num2中输入0.50,则循环不会中断。那我该怎么办呢?为什么它不能选择小数点前面的实际数字? - Zainab Nabeel
@Zainab,再次强调,这与_此_问题无关,您应该提出另一个问题,以便获得更多人的关注,而不仅仅是我。在这种情况下,如果您输入0.50,则会扫描int 0,并留下指针指向“.”。下一次扫描int时,由于“。”而无效。这将永远持续下去。 - paxdiablo

0

有多种选择,请看看我的。

while(1)
{
   printf("Enter a number\n");
   if(scanf("%d",&num2) == 1)
   {
     if(o == '/' && num2 == 0)
     {
       printf("You can't divide by zero\n");
     }
     else
     break;
   }
}

如果我使用浮点数,那么如何通过“==”符号来限制比较浮点数? - Zainab Nabeel
移除 continue 语句,这里用不到它。 - Spikatrix
@CoolGuy 你说得对,我也不明白为什么它应该在那里。 - Gopi

0

将其转换为以下形式的循环:

do {
    printf("Enter 2nd number: ");
    scanf("%d",&num2);
    if(o=='/' && num2==0 ){
        printf("You can't divide by zero \n");
        continue;
    } else {
        break;
    }
} while (1);

我也用浮点数编写了相同的代码,但浮点数与“==”符号不合理。那我该怎么办?@SMA - Zainab Nabeel
如果是这种情况,您可能需要查看此处 - SMA
如果我在num2中输入0.50,循环不会停止。那我该怎么办呢? - Zainab Nabeel
你的num2变量是整数类型,不是浮点类型,因此不要传递浮点值。 - SMA

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