Programmers rarely use the goto operator in modern programming, so you may skip this section if your instructor didn't explicitly assign it. Previously, the unrestrained use of goto statements caused many incomprehensible and unmaintainable programs. However, there remain at least two legitimate uses in contemporary programs: interrupting nested loops and implementing state machines.
A goto statement consists of the goto keyword, followed by a programmer-created identifier, and terminated with a semicolon. A label is any unique, legal identifier followed by a colon. Programmers may label any statement in a program and "jump" to it with a goto statement. Early versions of FORTRAN and the Basic programming language had few control statements (such as the ones introduced in this chapter). So, complex program control was implemented with labels and a tangled web of goto statements, resulting in what is now derisively called "spaghetti code" because the chaotic logic reminds programmers of a bowl of spaghetti.
some_statements; label1: statement1; ... if (...) goto label1; else goto label2; ... label2: statement2; |
while (...) { ... goto label; ... } some_statements; label: statement; |
some_statements; goto label; ... for (int i = 0; i < 100; i++) { ... label: statement; ... } |
(a) | (b) | (c) |
Interrupting a loop early (i.e., before the loop-control condition ends the loop "normally") allows programmers to write efficient code (see case analysis). break
and continue
are sufficient to interrupt a single, unnested loop. For example:
for (int i = 0; i < counter; i++) { . . . if (condition) break; . . . } |
for (int i = 0; i < counter; i++) { . . . if (condition) continue; . . . } |
(a) | (b) |
However, the break and continue statements only work for a single, unnested loop. If two or more loops are nested, break and continue only apply to the loop in which they are called (i.e., if called from a nested loop, they cannot affect an outer loop). Programmers typically use a goto statement when they need to break or continue out of nested or inner loops. The goto label merely provides a target for the goto operation: when the goto executes, the program transfers control to the statement following the label.
for (int i = 0; i < counter; i++) { for (int j = 0; j < c2; j++) { . . . if (condition) goto done; . . . } } done: |
int i = 0; next: for (; i < counter; i++) { for (int j = 0; j < c2; j++) { . . . if (condition) goto next; . . . } } |
(a) | (b) |
State machines are abstractions practitioners in many disciplines use to describe a system's dynamic behavior. They are most beneficial when an entity's response to an event depends on its response to previous events. Numerous state machine notations are in use, but all consist of finite sets of states and transitions. A state represents an entity's current condition or activity. A transition is a legal way the machine can change from one state to another. While the basic state machine notation is consistent across domains, variations exist. The following figure describes UML state diagrams.
Write a program that reads a C++ program file and separates the comments from the program code. The program should write the comments to one file and the program code to another file.
/* Separates comments from program code. Writes comments to one file, and the program * Code without comments is written to a second file. * Demonstration of using the goto statement to implement a state machine. Each state * is represented by a labeled section of code. Implements transitions with goto * statements. */ #include <iostream> #include <fstream> #include <string> using namespace std; int main() { // ---------------- opens files -------------------------------------- string program; cout << "Enter the input program file name: "; getline(cin, program); string comments; cout << "Enter the comments output file name: "; getline(cin, comments); string code_file; cout << "Enter the output file name: "; getline(cin, code_file); ifstream in(program); ofstream comm(comments); ofstream code(code_file); if (in.fail() || comm.fail() || code.fail()) { cerr << "Unable to open a file" << endl; return 1; } // ---------------- state machine begins ------------------------------ int c; // input character reading: // start state and reading program text c = in.get(); cout << "reading: " << (char)c << endl; switch (c) { case EOF: return 0; // no more input to process case '/': goto beginning; default: code.put(c); goto reading; } beginning: // possible beginning of a comment c = in.get(); switch (c) { case '/': goto cpp_comment; case '*': goto c_comment; default : // wasn't a comment code.put('/'); code.put(c); goto reading; } cpp_comment: // C++ style comment: // to end of line c = in.get(); switch (c) { case '\n': comm.put(c); code.put(c); goto reading; default: comm.put(c); goto cpp_comment; } c_comment: // C style comment (also supported by C++): /* to */ c = in.get(); switch (c) { case '*' : goto ending; default: comm.put(c); goto c_comment; } ending: // possible end of C style comment c = in.get(); switch (c) { case '/': comm.put('\n'); goto reading; default: // comment didn't end comm.put('*'); comm.put(c); goto c_comment; } }