为什么Linux内核里大量使用goto,而很多书籍却不提倡使用
关于C语言中的goto语句,存在着许多争议,很多书籍建议要“谨慎使用,甚至避免使用”。但是,在Linux之父Linus的实践中,他在Linux中广泛使用了goto语句,这也启示了我们可以合理地使用这个特性。正因为存在争议,我们有必要学会使用goto语句。下面来看一些goto语句的基本语法和示例:
一、goto的基本语法
goto语句由两部分组成:关键字goto和标签名。标签的命名规则与变量的命名规则相同。示例:
goto label;
要让这条语句正常工作,函数还必须包含另一条标为label的语句,该语句以标签名后紧跟一个冒号开始,如:
label:printf(“goto here.\n”);
二、goto的例子
左右滑动查看全部代码>>>
/*
编译环境:mingw32gcc6.3.0
*/
#include #include
/* goto测试 */
void TestGoto(void)
{
int i;
while (1)
{
for (i = 0; i if (i > 6)
{
goto label;
}
printf("%s : i = %d\n", __FUNCTION__, i);
}
}
label:
printf("test goto end!");
}
int main(void)
{
TestGoto();
}
运行结果:
image-20231018221244418
image-20231018221244418
从运行结果我们明显可以知道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 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!");
}
运行结果:
image-20231018221247903
image-20231018221247903
从运行结果我们明显可以知道,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 if (i > 6)
{
printf("i = %d, continue next loop\n", i);
continue; /* continue:结束本次循环(而不是终止这一层循环)继续进入下一次循环 */
}
printf("%s : i = %d\n", __FUNCTION__, i);
}
printf("test break end!");
}
运行结果:
image-20231018221251595
image-20231018221251595
从运行结果我们明显可以知道,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)
{
return0;
}
while(*str != 0 )
{
count++;
str++;
}
return count;
}
方法二:先设置一个变量,对变量赋值,只有一个return。
*左右滑动查看全部代码>>>*
int mystrlen(char *str)
{
int ret;
if (str == NULL)
{
ret = -1;
}
elseif (*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来处理错误!
页:
[1]