《C Primer Plus》(第6版)中文版 7.8章 goto语句
《JAVA核心技术 卷I:基础知识》3.8.6章 中断控制流程语句
##############################################################
最近一段时间,经常需要在同一个函数中编写多个重复代码,出现问题后就需要一个个找出来进行修改,没有效率。在一个例子里发现一段goto语句的使用,发现能够高效的处理重复代码,之前对于goto语句并没有在意,觉得应该不用任何goto语句,但是现在看来,goto语句还是很有效的
知乎上有一个关于goto语句使用的讨论:
既然建议尽量避免使用goto语句为何C++还要支持goto呢?:https://www.zhihu.com/question/21981058
################################################################
首先介绍C/C++版本的goto语句
goto语句也称为无条件转移语句,最早应该出现在BASIC和FORTRAN中,C/C++也能够使用goto语句,但C/C++与前面两种语言不同的是,goto语句并不是必需的,没有goto语句程序也能运行良好。
goto语句的一般格式:
goto 标签名;
其中标签名遵循变量命名规则
介绍一下变量命名规则:
1).变量名只能是字母(A-Z,a-z)和数字(0-9)或者下划线(_)组成;
2).第一个字母必须是字母或者下划线开头;
3).不能使用C/C++关键字来命名变量,以免冲突;
4).变量名区分大小写。
参考:C/C++变量命名规则,个人习惯总结 - http://blog.sina.com.cn/s/blog_8a7012cf01017h9p.html
同时程序必须包含另一条语句,格式为:
标签名:
即标签名后紧跟一个冒号
一个简单的C程序:
#include <stdio.h>
int main(int argc, char* argv[]) {
if (getchar() == 'a') {
goto Hi;
} else {
goto Hello;
}
Hi: printf("Hi World\n");
return 0;
Hello: printf("Hello World\n");
return 0;
}
当你输入字符a时,输出Hi World;否则,输出Hello World
原则上,根本不用在C/C++程序中使用goto语句。但在以下场景中,使用goto语句可以高效的处理代码
1.从多重循环中直接跳出
当需要结束嵌套循环时,break语句只能跳出当前循环,使用goto语句可以直接跳出所有循环,这样就不需要在嵌套循环中再进行if判断了
例子:从一个二重循环中退出
使用break
#include <stdio.h>
int main(int argc, char* argv[]) {
bool flag = false; //设置标志位,用于判断是否退出
int sum = 0;
// 累加i*j的值,大于10就结束
for (int i=0; i<10; i++) {
for (int j=0; j<10; j++) {
sum += i*j;
if (sum > 10) {
flag = true;
break;
}
}
if (flag) {
break;
}
}
printf("sum = %d\n", sum);
return 0;
}
使用goto
#include <stdio.h>
int main(int argc, char* argv[]) {
int sum = 0;
// 累加i*j的值,大于10就结束
for (int i=0; i<10; i++) {
for (int j=0; j<10; j++) {
sum += i*j;
if (sum > 10) {
goto exit;
}
}
}
exit:
printf("sum = %d\n", sum);
return 0;
}
很明显,goto语句能够方便的完成退出
2.为函数统一设置出口
这个就是我遇到的问题,不论退出函数时是好的结果还是坏的结果,都有可能会有一些必须执行的操作,比如释放资源,重设置标识符等。可能一个函数中会有多个出口,这样对于程序的可读性以及后续维护很不方便,使用goto可以很好的解决这个问题
不用goto
#include <iostream>
#include <string.h>
using namespace std;
string func(const char* arg1, const char* arg2);
int main(int argc, char* argv[]) {
string res;
res = func("arg1", "arg2");
cout <<"res = " <<res <<endl;
return 0;
}
string func(const char* arg1, const char* arg2) {
char* p1 = NULL;
p1 = (char*)malloc(sizeof(char) * strlen(arg1));
memset(p1, 0, strlen(arg1));
memcpy(p1, arg1, strlen(arg1));
char* p2 = NULL;
p2 =(char*)malloc(sizeof(char) * strlen(arg2));
memset(p2, 0, strlen(arg2));
memcpy(p2, arg2, strlen(arg2));
if (strcmp("arg1", p1) == 0) {
cout <<"arg1 ok" <<endl;
delete[] p1;
delete[] p2;
return arg1;
}
if (strcmp("arg2", p2) == 0) {
cout <<"arg2 ok" <<endl;
delete[] p1;
delete[] p2;
return arg2;
}
delete[] p1;
delete[] p2;
return "error";
}
使用goto
#include <iostream>
#include <string.h>
using namespace std;
string func(const char* arg1, const char* arg2);
int main(int argc, char* argv[]) {
string res;
res = func("arg1", "arg2");
cout <<"res = " <<res <<endl;
return 0;
}
string func(const char* arg1, const char* arg2) {
char* p1 = NULL;
p1 = (char*)malloc(sizeof(char) * strlen(arg1));
memset(p1, 0, strlen(arg1));
memcpy(p1, arg1, strlen(arg1));
char* p2 = NULL;
p2 =(char*)malloc(sizeof(char) * strlen(arg2));
memset(p2, 0, strlen(arg2));
memcpy(p2, arg2, strlen(arg2));
string res = "error";
if (strcmp("arg1", p1) == 0) {
cout <<"arg1 ok" <<endl;
res = arg1;
goto exit;
}
if (strcmp("arg2", p2) == 0) {
cout <<"arg2 ok" <<endl;
res = arg2;
goto exit;
}
exit:
delete[] p1;
delete[] p2;
return res;
}
将同一个函数中的所有出口统一到一起,可读性提高,同时方便今后修改
#############################################################
Java中将goto作为关键字,但并没有给它赋予任何功能,但在Java中也有可替代goto功能的语句,就是带标签的break和带标签的continue语句
参考:Alternative to a goto statement in Java - http://stackoverflow.com/questions/2430782/alternative-to-a-goto-statement-in-java/2430829#2430829
带标签的break语句
语法:
break 标签名;
标签名:
注意:第二条语句必须放在希望跳出的语句块之前,并且必须紧跟一个分号,同时语句块中必须包含带标签的break语句,所以该语句仅能作为跳出语句块的操作。语句块可以使多重循环,也可以是if语句,for语句或者{}块语句。
一个简单的例子:输入数字,小于等于0则退出
package com.zj;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// write your code here
Scanner in = new Scanner(System.in);
int n = 0;
read_data:
while (true) {
while (true) {
System.out.print("Enter a number >= 0:");
n = in.nextInt();
if (n <= 0)
break read_data;
}
}
if (n < 0) {
System.out.println("n < 0");
} else if (n == 0) {
System.out.println("n == 0");
}
}
}
还有一个continue加标签的用法,用于退出当前循环,继续上层循环,想要了解的可以查找相关资料