goto
statements are not typically covered in introductory courses and you may skip this section if it was not explicitly assigned.
The goto
statement is rarely used anymore and therefore rarely covered. It is, deservedly, much-maligned. Its unrestrained use has resulted in too many incomprehensible and unmaintainable programs. However, there remain at least two legitimate uses in contemporary programming languages like C++: interrupting nested loops and implementing state machines.
A label is any unique, legal identifier followed by a colon. A goto statement is the goto keyword followed by a label and terminated with a semicolon. Programmers may label any statement in a program, and they may arbitrarily "jump" to the labeled statement with a goto statement. Early versions of FORTRAN and the Basic programming language had if-statements but not loops. So, program control was implemented by a tangled web of goto statements. The resulting programs were often described as "spaghetti code" because the logic resembled a tangled bowl of spaghetti.
label1: statement1; ... if (...) goto label1; else goto label2; ... label2: statement2; |
while (...) { ... goto label; ... } label: statement; |
goto label; ... for (int i = 0; i < 100; i++) { ... label: statement; ... } |
(a) | (b) | (c) |
Either break
or 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 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, control is transfered 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) |
A state machine is an abstraction used in many disciplines to describe the dynamic behavior of some component implemented in software or hardware (or both). They are composed of finite sets of states and transitions. A state represents the entity's current condition or activity. A transition is a legal way that the machine can change from one state to another. Events, such a reading an input, trigger transitions. Programmers can add detail to a transition with three optional features delimited by three symbols: [, ], and /.
event [ guard condition ] / action
State machines are most useful in cases where a component's response to an event depends on how it responded to previous events. There are numerous state machine notations in use. The UML state diagram represents each state as a rectangle with rounded corners and each transition as a labeled arrow from a source to a destination state.
entry
exit
do
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. Comments are written to one file, and 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. Transitions are implemented 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 program code 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; } }