C/C++ GOTO妙用
GOTO 语句
C/C++ 的 goto 语句用来在一个函数内进行任意跳转,用起来也是很方便。示例如下:
int a() {
int x = 0, sum = 0;
L1: if (x < 10) {
x++;
sum += x;
goto L1;
}
return sum;
}
只需要在函数内部某处加一个标签(Label),通过 goto <label> 即可直接跳转。
但是由于 goto 的跳转比较原始,没有结构化,当程序比较庞大的时候调试起来就比较麻烦,代码可读性会大大降低,因此 E.W.Dijikstra 在 1965 年提出结构化程序设计来规避这种错误,并使代码更易读。很多时候我们尽量避免使用 goto 语句,转而使用其他的结构化方式。
尽管如此,也有一些地方使用 goto 语句相比结构化的方式更加高效。以下给出若干例子。
跳出多层循环
跳出循环的语句是 break,跳过一次循环的语句是 continue,但它们仅能跳出/跳过一层循环。当需要跳出多层循环时,可以考虑使用 goto 语句:
LOOP: for (...) // loop 1
for (...) // loop 2
for (...) // loop 3
if (...)
goto LOOP;
此处的 goto 语句从第三层循环跳到第一层循环,相当于在第一层循环下执行了 continue 语句。而如果要用结构化的方式来实现,往往要借助条件变量。尽管如此,仍然需要一次次地跳出当前循环:
int flag = 0;
for (...) // loop 1
for (...) // loop 2
for (...) { // loop 3
if (...) {
flag = 1;
break;
}
}
if (flag) break;
}
}
可以看到,这次除了引入一个 flag 变量外,用了两个 break 语句才实现了等价功能。
很多高级语言都不支持 goto 语句以防止可能遇到的问题,比如 Java。但是 Java 支持用 break <label> 和 continue <label> 来支持多层循环的跳出与跳过,比如该 goto 语句示例在 Java 中的等价写法是把 goto 关键字换成 continue。因此,这种用法不应该被嫌弃。
循环首次部分跳过
如果把一个循环体分为前后两部分 A 和 B,那么循环展开后的执行情况为:
ABABABAB...ABAB
但是,很多情况下我们可能要求跳过第一次运行的 A 或者最后一次运行的 B,那么实际的运行情况为:
BABABAB...ABAB
最经典的例子就是打印一个数组且要求结尾没有空格。此时可以考虑使用 goto 语句:
void print_array(int arr[], int n) {
int i = 0;
if (i<n) goto PNT;
for (; i<n; i++) {
putchar(' '); // A
PNT:
printf("%d", arr[i]); // B
}
puts(); // 换行
}
此时循环展开就完美按照 BABABA...BA 的顺序执行以打印数组。如果不用 goto 语句,那么可以考虑两种方案:
- 给 A 部分加上条件判断是否为第一次循环。但这样的话,每次循环时都会进行判断,造成不必要的判断过程;
- 直接把 B 部分代码放到循环前面。但是当 B 部分代码特别多时,该方法会导致严重的代码重复。尽管可以使用函数将 B 部分封装,但是函数调用也会带来额外开销。
相比之下,这种情况使用 goto 语句完全不影响循环体代码,效率更高。
注意,这种情况下要求循环体的循环条件语句尽量简单(大多数情况下是简单的,比如此处的 i<n),因为对于 for、while 等循环会在进入循环前判断一次,因此这种写法通常会在 goto 前加上 if 语句进行同样的循环判断。如果判断语句过长过复杂,同样会造成一定的代码冗余。当然,对于 do-while 循环,其循环条件是在每次循环后判断,此时不必在 goto 跳转前进行判断。
另外,该方法在编写代码时,如果要达到 ABABAB...ABA 的效果,即跳过最后一次循环的后半部分,应该直接在循环体内将 A、B 的顺序调换,并用 goto 语句跳过第一次循环的前半部分。
原文地址:https://www.cnblogs.com/RainbowC0/p/18765900 ,未经作者许可禁止转载。
C/C++ GOTO妙用的更多相关文章
- C语言中do...while(0)的妙用-避免goto
使用goto的优雅并避免结构的混乱 将要跳转到的语句用do{-}while(0) 包起来就可以. reference #defien N 10 bool Execute() { // 分配资源 int ...
- C语言中do...while(0)的妙用(转载)
转载来自:C语言中do...while(0)的妙用,感谢分享. 在linux内核代码中,经常看到do...while(0)的宏,do...while(0)有很多作用,下面举出几个: 1.避免goto语 ...
- 求1+2+…+n,要求不能使用乘除法、for、while、if、else、s witch、case 等关键字以及条件判断语句(A?B:C)和不用循环/goto/递归输出1~100的10种写法
来源:据说是某一年某个公司的面试题 题目:求1+2+…+n, 要求不能使用乘除法.for.while.if.else.s witch.case 等关键字以及条件判断语句(A?B:C) 分析:这题本来很 ...
- C语言 goto语句
/* goto语句 */ #include <stdio.h> #include <stdlib.h> #include <string.h> /* goto语句也 ...
- 【C++基础 03】do...while(0)妙用
我的主题是,有时候知道一些细节会让你写出更好的代码. ============================================ 之前学coocs2d-x的时候,发现有非常多do...w ...
- windows脚本(VBS)之cmd命令行的妙用
windows脚本(VBS)之cmd命令行的妙用 (2009-08-06 13:40:55) 转载▼ 标签: 脚本 cmd 命令行 vbs js 简单 公式 windows it 分类: 计算机 脚本 ...
- 【转】do...while(0)的妙用
前言 今天无意中看到这个标题,因为好奇就点进去了,不错,又学习啦... 具体内容: 1. do...while(0)消除goto语句: 2 宏定义中的do...while(0): 参考 1. 原链接_ ...
- 臭名远扬之 goto 语句
C 语言自学之 goto 语句 Dome1:以下程序实现从控制台输出1-10,使用goto语句,实现当输出完3之后跳出循环体. 1 #include <stdio.h> 2 3 int m ...
- 【CSS进阶】伪元素的妙用--单标签之美
最近在研读 <CSS SECRET>(CSS揭秘)这本大作,对 CSS 有了更深层次的理解,折腾了下面这个项目: CSS3奇思妙想 -- Demo (请用 Chrome 浏览器打开,非常值 ...
- angular2系列教程(十)两种启动方法、两个路由服务、引用类型和单例模式的妙用
今天我们要讲的是ng2的路由系统. 例子
随机推荐
- Docker非root用户修改/etc/hosts文件
本文会讲解在Docker容器环境下,非root用户如何编辑修改/etc/hosts文件. 1.背景和需求描述 环境:Docker 运行用户:非root用户,如普通用户1001 需求:应用运行在容器内, ...
- 报错test_features2d.cpp:51:10: fatal error: features2d/test/test_detectors_regression.impl.hpp: 没有那个文件
问题描述: ubuntu18.04安装opencv4.5.1+contrib 报错test_features2d.cpp:51:10: fatal error: features2d/test/tes ...
- 网络编程入门如此简单(四):一文搞懂localhost和127.0.0.1
本文由萤火架构分享,原题"localhost和127.0.0.1的区别是什么?",原文链接"juejin.cn/post/7321049446443417638" ...
- 基于开源IM即时通讯框架MobileIMSDK:RainbowChat-iOS端v8.0版已发布
关于MobileIMSDK MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架,超轻量级.高度提炼,一套API优雅支持 UDP .TCP .WebSocket 三种协议,支持 iOS ...
- 某苏人社异步JS逆向加解密分析(sm2+sm3+sm4)
前期准备 网址:aHR0cHM6Ly9ycy5qc2hyc3MuamlhbmdzdS5nb3YuY24vaW5kZXgv 目标: 接口请求内容加密和响应内容解密 请求头加密参数 Web-Encrypt ...
- Linux C语言面试考点
数组 数组初始化方法 /* 以下为自动类型 *//* 一维数组 */int arr[] = {1, 3, 5}; //不指定长度,由编译器自动计算int arr[5] = {0, }; //指定长度 ...
- 1273 - Unknown collation: 'utf8mb4_0900_ai_ci'
Navicat导出的MySQL8.0脚本在低版本MySQL导入执行报错 utf8mb4_0900_ai_ci 替换 utf8_general_ci utf8mb4 替换 utf8 完成以上两点替换再次 ...
- java线程用法和区别
从操作系统的角度讲,os会维护一个ready queue(就绪的线程队列).并且在某一时刻cpu只为ready queue中位于队列头部的线程服务.但是当前正在被服务的线程可能觉得cpu的服务质量不够 ...
- struts2框架详解
struts2框架(1)---struts2入门 struts2框架 如果你之前在MVC模式的时候一直都是通过servlet,获取和返回数据,那么现在开始学习struts2框架, Struts是一个实 ...
- javascript对象学习笔记
目前所见的,在普通的JavaScript应用中和绝大多数人一样使用的是全局的方式.大概是因为JavaScript所定义的变量或函数默认就是全局的.为了是代码不至于太凌乱,变量不至于稀里糊涂的就被人重定 ...