The goto statement jumps to a label in the same function—no condition attached. Most beginner code should use if, loops, and break instead, but goto still appears in cleanup patterns and for escaping deeply nested loops. This tutorial explains syntax, legitimate uses, pitfalls, five examples, and when not to use it.
01
Label
name:
02
goto
Jump there.
03
Same fn
Only local.
04
Nested
Exit loops.
05
Cleanup
Error path.
06
Sparingly
Rare use.
Fundamentals
Definition and Usage
The goto statement transfers control to a labeled statement in the same function. A label is an identifier followed by a colon (done:). The jump is unconditional—unlike if, nothing is tested at the goto itself.
Structured programming favors if, for, while, and functions. Still, you may see goto in Linux kernel code and in centralized cleanup blocks that free memory or close files after failures. Know what it does; use it only when the benefit is clear.
💡
Beginner Tip
If you reach for goto to fix messy logic, try extracting a function or using a break with a flag first. Reserve goto for patterns like “jump to cleanup at the bottom of one function.”
Foundation
📝 Syntax
General form of goto and a label:
C
goto label_name;
/* ... other statements ... */
label_name:
statement;
Rules
Same function — label and goto must live in one function.
Forward or backward — both directions are legal in C.
Label scope — visible throughout the enclosing function.
Not into a block — cannot jump into the middle of a scope that skipped initialization (compiler may reject).
Compile
C
gcc program.c -std=c11 -o program
Cheat Sheet
⚡ Quick Reference
Pattern
Example
Forward jump
goto done; ... done: return 0;
Exit nested loops
goto exit_loops; after match
Error cleanup
if (fail) goto cleanup; ... cleanup: free(p);
vs break
break exits one loop; goto can leave several
Invalid
Jump to label in another function
Syntax
goto L;
Unconditional
Label
L:
Target
Scope
one function
Same fn only
Prefer
if / break
Usually
Hands-On
Examples Gallery
Compile with gcc example.c -std=c11 -o example. Each program shows a different goto pattern—study them, but prefer structured code in your own projects.
📚 Getting Started
Classic nested-loop exit and error handling from the reference material.
Example 1 — Exit Nested Loops
break only leaves the inner loop; goto can leave both at once.
C
#include <stdio.h>
int main(void) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) {
goto exit_loops;
}
printf("i = %d, j = %d\n", i, j);
}
}
exit_loops:
printf("Exited the loops\n");
return 0;
}
📤 Output:
i = 0, j = 0
i = 0, j = 1
i = 0, j = 2
i = 1, j = 0
Exited the loops
How It Works
At i == 1 and j == 1, execution jumps to exit_loops: and skips the rest of both loops. A done flag checked in both loop headers achieves the same without goto.
Example 2 — File Open Error Handling
Jump to a shared error block when fopen fails.
C
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *file = fopen("missing.txt", "r");
if (file == NULL) {
goto error;
}
/* would read/process file here */
fclose(file);
return 0;
error:
fprintf(stderr, "Failed to open the file\n");
return 1;
}
📤 Output (stderr):
Failed to open the file
How It Works
When the file is missing, fopen returns NULL and goto error runs the cleanup message. In small programs, a simple if (file == NULL) { ... return 1; } is often enough without goto.
📈 Practical Patterns
Forward jumps, search, and multi-step cleanup.
Example 3 — Forward Jump to End
Skip remaining steps when a value is invalid.
C
#include <stdio.h>
int main(void) {
int n = -5;
if (n < 0) {
printf("Invalid input: %d\n", n);
goto end;
}
printf("Processing %d...\n", n);
printf("Done processing.\n");
end:
printf("Program finished.\n");
return 0;
}
📤 Output:
Invalid input: -5
Program finished.
How It Works
goto end skips “Processing” lines. An else branch or early return would be clearer here—this shows forward jumps only.
Example 4 — Find Value in a Matrix
Search a 2D array and leave both loops when the target is found.
C
#include <stdio.h>
int main(void) {
int grid[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int target = 5;
int found_row = -1, found_col = -1;
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++) {
if (grid[r][c] == target) {
found_row = r;
found_col = c;
goto found;
}
}
}
found:
if (found_row >= 0) {
printf("Found %d at row %d, col %d\n", target, found_row, found_col);
} else {
printf("%d not found\n", target);
}
return 0;
}
📤 Output:
Found 5 at row 1, col 1
How It Works
Without goto, you would set a found flag and break from the inner loop, then break the outer loop if the flag is set. goto found is compact for a one-off search.
Example 5 — Centralized malloc Cleanup
One label frees everything when any allocation step fails.
C
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *a = NULL;
int *b = NULL;
int status = 0;
a = malloc(10 * sizeof *a);
if (a == NULL) {
status = 1;
goto cleanup;
}
b = malloc(10 * sizeof *b);
if (b == NULL) {
status = 2;
goto cleanup;
}
a[0] = 42;
printf("Allocated and used both buffers.\n");
cleanup:
free(b);
free(a);
if (status != 0) {
printf("Cleanup after error code %d\n", status);
}
return status;
}
📤 Output (success path):
Allocated and used both buffers.
How It Works
cleanup: always runs free on pointers (safe on NULL). This pattern appears in kernel and library C code. In application code, smaller functions or a do { ... } while(0) macro sometimes replace it.
Applications
🚀 Common Use Cases
Nested loop exit — leave two or more loops at once.
Resource cleanup — one block frees memory, closes files.
Error paths — jump to shared failure handling in long functions.
Kernel/driver code — low-level C where structured escape is awkward.
Generated parsers — some tools emit goto for state machines.
Avoid in app logic — menus and business rules belong in if/switch.
🧠 How goto Works
1
Define a label
label_name: marks a jump target in the function.
Label
2
Execute goto
goto label_name; runs when reached (often after an if).
Jump
3
Skip intermediate code
Statements between goto and the label do not run.
Bypass
=
➜
Continue at label
Execution resumes at the labeled statement and proceeds normally afterward.
Important
📝 Notes
goto cannot cross function boundaries.
Labels do not change scope—variables declared after a label were not initialized if you jumped over their declaration.
Dijkstra’s famous note argued against unrestricted goto; modern style favors structure.
break and continue are not substitutes for cross-function exit—use return.
Name labels clearly: cleanup:, error:, not single letters in large files.
goto is a direct jump at machine level—no meaningful speed advantage over well-written if and loops on modern compilers. Readability and maintainability matter far more. Choose structure over micro-optimization.
Wrap Up
Conclusion
goto jumps to a label in the same function. It can simplify nested-loop exit and centralized cleanup, but overuse creates spaghetti code. Default to if, loops, break, and return—know goto so you can read real-world C, not so you can sprinkle it everywhere.
You have finished the control statements track. Continue with C Loops to master repetition next.
goto transfers control unconditionally to a label in the same function. Syntax: goto label_name; and label_name: before a statement. Execution continues at the label.
No. goto and its label must be in the same function. You cannot jump into or out of a function with goto. Use return for leaving functions.
Yes. goto can jump forward or backward within a function, as long as the label is in scope. Backward jumps can create loops but are usually clearer written as for or while.
Rarely. Common acceptable uses: centralized cleanup after failed steps (free resources), breaking out of deeply nested loops, and some kernel/driver code. Prefer if, loops, break, and return in beginner programs.
Spaghetti code jumps unpredictably with many gotos, making flow hard to follow. Excessive goto creates this style. Structured control (if, for, while, functions) keeps programs readable.
Use break or a flag for nested loops, return for leaving functions, else-if or switch for branching, and small helper functions to simplify complex flow. break exits one loop; goto can exit several in one jump.
Did you know?
C++ added restrictions so you cannot goto over declarations that need constructors—plain C has fewer rules, but jumping past int x = 5; without running the initializer is still undefined behavior territory. Keep labels below your declarations or use blocks carefully.