【C语言笔记】goto语句

关于C语言的goto语句存在很多争议,很多书籍都建议“谨慎使用,或者根本不用”。这里先不做过多的讨论,存在即合理,既然是C语言中的一个知识点,我们还是有必要学会使用。先看一些goto如何用:

一、goto的基本语法

goto语句有两部分:goto和标签名。标签的命名规则与变量的命名规则一样。如:

goto label;

要让这条语句正常工作,函数还必须包含另一条标为label的语句,该语句以标签名后紧跟一个冒号开始,如:

label:printf(“goto here.\n”);

二、goto的例子

/*
编译环境:mingw32  gcc6.3.0
*/
#include <stdio.h>
#include <stdlib.h>

/* goto测试 */
void TestGoto(void)
{
    int i;

    while (1)
    {
        for (i = 0; i < 10; i++)
        {
            if (i > 6)
            {
                goto label;
            }
            printf("%s : i = %d\n", __FUNCTION__, i);
        }
    }
    label:
        printf("test goto end!");
}

int main(void)
{
    TestGoto();
}

运行结果:

从运行结果我们明显可以知道goto用法,可以跳出多重循环,程序执行过程中遇到goto语句就可以跳转到label处继续执行。值得注意的一点是:goto语句与其跳转的标签处必须在同一个函数内。

三、goto与break、continue的区别?

同样是跳转语句,goto语句与break、continue语句有什么区别呢?实际上,break和continue是goto的特殊形式。使用break与continue的好处是:其名称已经表明他们的用法。下面通过代码实例看一下break与continue的用法:

1、break测试函数

使用上面的测试程序,建一个测试break语句的函数void TestBreak(void);,如:

/* break测试 */
void TestBreak(void)
{
    int i;

    while (1)
    {
        for (i = 0; i < 10; i++)
        {
            if (i > 6)
            {
                break;    /* 第一个break:跳出for循环 */
            }
            printf("%s : i = %d\n", __FUNCTION__, i);
        }
        printf("Now i = %d\n", i);
        break;           /* 第二个break:跳出while循环 */    
    }
    printf("test break end!");
}

运行结果:

从运行结果我们明显可以知道,break可以退出当前循环。在本例子中,第一个break语句退出当前的for循环,第二个break语句退出当前的while循环。可见,一个break可退出一层循环。所以,根据break与goto的特点知道,如果是跳出很多层循环,使用goto会方便些。

2、continue测试函数

同样的,建一个测试continue语句的函数void TestContinue(void);,如:

/* continue测试 */
void TestContinue(void)
{
    int i;

    for (i = 0; i < 10; i++)
    {
        if (i > 6)
        {
            printf("i = %d, continue next loop\n", i);
            continue;    /* continue:结束本次循环(而不是终止这一层循环)继续进入下一次循环 */
        }
        printf("%s : i = %d\n", __FUNCTION__, i);
    }
    printf("test break end!");
}

运行结果:

从运行结果我们明显可以知道,continue可以结束本次循环(而不是整个循环)而进入下一次循环(i所代表的就是循环的次数)。

四、支持与反对goto的理由是什么?

1、不提倡使用goto

不提倡使用goto的占比应该比较多,不提倡的原因主要是:

  • 很容易把逻辑弄乱且难以理解。

2、使用goto的理由

这一部分人认为goto可以用在以下两种情况比较方便:

(1)跳出多层循环。

这个例子就类似于我们上面的goto测试程序。

(2)异常处理。

一个函数的执行过程可能会产生很多种情况异常情况。下面有几种处理方式,以代码为例:

方法一:做出判断后,如果条件出错,直接return。

int mystrlen(char *str)
{  
   int count = 0;
   if (str == NULL)
   {
      return -1;
   }  

   if (*str == 0)
   {
      return 0;
   }  

   while(*str != 0 )
   {
      count++;
      str++;
   }  
   return count;
}

方法二:先设置一个变量,对变量赋值,只有一个return。

int mystrlen(char *str)
{  
   int ret;
   if (str == NULL)
   {
      ret = -1;
   }  
   else if (*str == 0)
   {
      ret = 0;
   }  
   else
   {
      ret = 0;
      while(*str != 0 )
      {
         ret++;
         str++;
      }
   }
   return ret;
}

方法三:使用goto语句。

int mystrlen(char *str)
{
   int ret;
   if (str == NULL)
   {
      ret = -1;
      goto _RET;
   }

   if (*str == 0)
   {
      ret = 0;
      goto _RET;
   }


   while(*str !=0 )
   {  
      ret++;
      str++;
   }  

_RET:
   return ret;
}

其中,方法三就是很多人都提倡的方式,统一用goto err跳转是最方便且效率最高的,从反汇编语句条数可以看出指令用的最少,消耗的寄存器也最少,效率无疑是最高的。并且,使用goto可以使程序变得更加可扩展。当程序需要在错误处理时释放资源时,统一到goto处理最方便。这也是为什么很多大型项目,开源项目,包括Linux,都会巨量的出现goto来处理错误!

以上就是关于goto语句的基本使用、goto与break、continue的区别的总结,如有错误,欢迎指出!如果觉得不错可以帮忙分享~

参考资料:

1、《C Primer Plus》
2、https://www.cnblogs.com/trying/archive/2012/06/25/2863753.html

我的个人博客:https://zhengnianli.github.io/

我的微信公众号:嵌入式大杂烩


 上一篇
【STM32笔记】什么是通讯? 【STM32笔记】什么是通讯?
一、什么是串行通讯与并行通讯?按数据传送的方式,通讯可分为串行通讯与并行通讯,串行通讯是指设备之间通过少量数据信号线(一般是 8 根以下),地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式。 而并行通讯一般是指使用 8、 16、
2019-08-23
下一篇 
【STM32笔记】串口空闲中断接收不定长数据(DMA方式) 【STM32笔记】串口空闲中断接收不定长数据(DMA方式)
在使用STM32的串口接收数据的时候,我们常常会使用接收中断的方式来接收数据,常用的是RXNE。这里分享另一种接收数据的方式——IDLE中断(PS:本文的例子运行在STM32F103ZET6上)。 一、IDLE中断什么时候发生?IDLE就是
2019-08-18
  目录