Jump to content

C++ Programming

From Wikibooks, open books for an open world

The goto keyword is discouraged as it makes it difficult to follow the program logic, this way inducing to errors. The goto statement causes the current thread of execution to jump to the specified label.

Syntax
label:
  statement(s);

goto label;

In some rare cases, the goto statement allows to write uncluttered code, for example, when handling multiple exit points leading to the cleanup code at a function exit (and neither exception handling or object destructors are better options). Except in those rare cases, the use of unconditional jumps is a frequent symptom of a complicated design, as the presence of many levels of nested statements.

In exceptional cases, like heavy optimization, a programmer may need more control over code behavior; a goto allows the programmer to specify that execution flow jumps directly and unconditionally to a desired label. A label is the name given to a label statement elsewhere in the function.

Note:
There is a classic paper in software engineering by W. A. Wulf called "A case against the GOTO", presented in the 25th ACM National Conference in October 1972, a time when the debate about goto statements was reaching its peak. In this paper Wulf defends that goto statements should be regarded as dangerous. Wulf is also known by one of his comments regarding efficiency: "More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason -- including blind stupidity.".

A goto can, for example, be used to break out of two nested loops. This example breaks after replacing the first encountered non-zero element with zero.

for (int i = 0; i < 30; ++i) {
  for (int j = 0; j < 30; ++j) {
    if (a[i][j] != 0) {
       a[i][j] = 0;
       goto done;
     }
  }
}
done:
/* rest of program */

Although simple, they quickly lead to illegible and unmaintainable code.

// snarled mess of gotos

int i = 0;
  goto test_it;
body:
  a[i++] = 0;
test_it:
  if (a[i]) 
    goto body;
/* rest of program */

is much less understandable than the equivalent:

for (int i = 0; a[i]; ++i) {
  a[i] = 0;
}
/* rest of program */

Gotos are typically used in functions where performance is critical or in the output of machine-generated code (like a parser generated by yacc.)

The goto statement should almost always be avoided, but there are rare cases where it enhances the readability of code. One such case is an "error section".

Example

#include <new>
#include <iostream>

...

int *my_allocated_1 = NULL;
char *my_allocated_2 = NULL, *my_allocated_3 = NULL;
my_allocated_1 = new (std::nothrow) int[500];

if (my_allocated_1 == NULL)
{  
  std::cerr << "error in allocated_1" << std::endl;
  goto error;
}

my_allocated_2 = new (std::nothrow) char[1000];

if (my_allocated_2 == NULL)
{  
  std::cerr << "error in allocated_2" << std::endl;
  goto error;
}
    
my_allocated_3 = new (std::nothrow) char[1000];

if (my_allocated_3 == NULL)
{  
  std::cerr << "error in allocated_3" <<std::endl;
  goto error;
}
return 0;
    
error:
  delete [] my_allocated_1;
  delete [] my_allocated_2;
  delete [] my_allocated_3;
  return 1;

This construct avoids hassling with the origin of the error and is cleaner than an equivalent construct with control structures. It is thus less error prone.

Note:
While the above example shows a reasonable use of gotos, it is uncommon in practice. Exceptions handle such cases in a clearer, more effective and more organized way. This will be discussed in "Exception Handling" in detail. Using RAII to manage resources such as memory also avoids the need for most of the explicit cleanup code that is shown above.