Goto Statement in C

The break and continue statements are jump statements, that is, special control flow statements that allow jumping from one point in the code to another.

However, both statements are limited:

  • In the case of the break statement, the jump occurs only from inside a loop or a switch to the statement immediately following it;
  • In the case of the continue statement, the jump occurs only within a loop, skipping the current iteration and moving to the next one.

The goto statement, on the other hand, allows jumping to any statement in the code provided that this is a statement equipped with a label.

Although the use of the goto statement is generally discouraged and is a remnant of older programming languages, it is still present in C and can be used for specific purposes. In this lesson, we will see how the goto statement works in C and how to use it correctly.

Labels

First of all, it is important to understand what a label of a statement in C language is.

In C we can assign an identifier to any statement in the code, thus creating a label. A label is an identifier followed by a colon (:) and can be positioned before any statement.

The syntax for defining a label is as follows:

label_name: statement;

For example, we can define a label called start before a printf statement:

#include <stdio.h>
int main() {
    start: // Label called "start"
    printf("This is the start of the program.\n");
    return 0;
}

In this example, the start label is defined before the printf statement.

A statement can have multiple labels, as long as they are unique within the code. For example:

#include <stdio.h>

int main() {
    start: // Label called "start"
    start2: // Label called "start2"
    printf("This is the start of the program.\n");
    return 0;
}
Definition

Label (Label)

In C, a label is an identifier followed by a colon (:) that allows referring to a specific statement in the code.

The syntax for defining a label is:

label_name: statement;

Labels in C, apart from assigning a name to a statement, have no effect on the program flow. However, they essentially have two main purposes:

  1. They are used for switch constructs:

    Labels are used to identify cases within a switch construct, as we have already seen, allowing the program to jump directly to the corresponding case.

  2. They are used with the goto statement:

    Labels are used to define jump points for the goto statement, allowing the program to jump to a specific statement in the code.

Having already seen the first case, now let's see how to use labels with the goto statement.

goto Statement

The goto statement allows jumping to a specific label in the code, executing the statement that follows the label. The syntax for using the goto statement is as follows:

goto label_name;

For example, we can use the goto statement to jump to a label called end:

#include <stdio.h>

int main() {
    printf("Start of the program.\n");

    goto end; // Jump to the "end" label

    // This statement will not be executed!
    printf("This statement will not be executed.\n");

    end: // Label called "end"
    printf("End of the program.\n");

    return 0;
}

In this example, the goto end; statement jumps directly to the end label, causing the printf("This statement will not be executed.\n"); statement not to be executed.

Definition

goto Statement

The goto statement in C allows jumping to a specific label in the code, executing the statement that follows the label.

The syntax for using the goto statement is:

goto label_name;

As can be seen, the goto statement allows jumping to any point in the code that has a defined label, making the program flow less linear and more difficult to follow. Therefore, the use of the goto statement is generally discouraged, as it can make the code less readable and more difficult to maintain.

For example, let's consider the following code:

#include <stdio.h>

int main() {
    int i = 0;

    for (i = 0; i < 5; i++) {
        printf("Iteration %d\n", i);
    }

    return 0;
}

This code executes a for loop that prints iterations from 0 to 4. It is simple code and above all easy to read and understand.

We could rewrite the same code using the goto statement, but the result would be much less clear:

#include <stdio.h>

int main() {
    int i = 0;

    start: // Label called "start"
    if (i < 5) {
        printf("Iteration %d\n", i);
        i++;
        goto start; // Jump to the "start" label
    }

    return 0;
}

It is effectively an "unstructured" jump statement, a remnant of older programming languages.

Note

Avoid Using the goto Statement

The use of the goto statement in C is strongly discouraged, as it can make the code less readable and more difficult to maintain. It is preferable to use control flow structures such as if, for, while and switch to manage the program flow in a clearer and more structured way.

That said, it should be kept in mind that the C language still imposes constraints on the use of the goto statement:

Definition

Constraints of the goto Statement

  • The goto statement can only jump to labels that are in the same function where it was declared;
  • In C99 and later versions, the goto statement cannot bypass the declaration of a variable length array.

When to Use the goto Statement

Although the use of the goto statement is generally discouraged, there are some cases where it can still be useful.

The first case concerns exiting from nested loops or code blocks. For example, let's consider the following code that uses a nested loop:

for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
        if (i == 2 && j == 2) {
            /* ... */
            break;
        }
        printf("i: %d, j: %d\n", i, j);
    }
}

In this case, the break statement interrupts only the inner loop, but not the outer one.

If we wanted to interrupt both loops, we could use the goto statement to jump to a label that is outside both loops:

for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
        if (i == 2 && j == 2) {
            goto end; // Jump to the "end" label
        }
        printf("i: %d, j: %d\n", i, j);
    }
}

end: // Label called "end"
printf("Exited from loops.\n");

In this case, the goto statement allows exiting from both loops in a simple and direct way.

Another case where the goto statement can be useful is when you want to handle errors or special conditions in a centralized way. For example, you could use goto to jump to a cleanup label at the end of a function, avoiding code duplication of cleanup code in different points of the function.

#include <stdio.h>

int main() {
    FILE *file = fopen("file.txt", "r");
    if (file == NULL) {
        printf("Error opening file.\n");
        return 1;
    }

    // Other operations on the file...

    if (/* error condition */) {
        goto cleanup; // Jump to the "cleanup" label
    }

    // Other operations...
    cleanup: // Label called "cleanup"
    fclose(file); // Close the file
    printf("File closed correctly.\n");
    return 0;
}

We haven't studied files in C yet, but the idea is that the goto statement allows jumping directly to the cleanup section of the code, avoiding duplication of file closing code in case of errors.

A final case where the goto statement can be useful is when you want to have maximum control over code performance, for example in low-level programming situations or in performance optimization contexts. However, these cases are rare and require a good understanding of the code, its implications and above all the underlying architecture both in terms of processor and operating system.

In general, the use of the goto statement should be limited to situations where more structured alternatives are not practicable or do not offer the same flexibility. However, it is important to remember that excessive use of goto can lead to code that is difficult to read and maintain, so it is always advisable to carefully evaluate whether it is really necessary to use it.