c-goto-error-handling
Table of Contents
C goto error handling
C goto error handling uses goto labels to centralize cleanup code, avoiding nested error handling and ensuring resources are freed consistently. Each operation jumps to the appropriate cleanup label on error, executing only the necessary cleanup steps in reverse order.
Use goto error_label: in C to handle cleanup elegantly without repetition or deeply nested conditionals.
Example
This example shows goto for resource cleanup with proper unwinding.
// compile: gcc -o goto_error goto_error.c // run: ./goto_error // description: error handling with goto for cleanup #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { FILE *f; int *buffer; } Resources; int read_config(const char *filename, Resources *res) { memset(res, 0, sizeof(*res)); // Open file res->f = fopen(filename, "r"); if (res->f == NULL) { fprintf(stderr, "Failed to open %s\n", filename); goto error; } // Allocate buffer res->buffer = malloc(1024 * sizeof(int)); if (res->buffer == NULL) { fprintf(stderr, "Failed to allocate buffer\n"); goto error_close_file; } // Read data int count = fread(res->buffer, sizeof(int), 100, res->f); if (count != 100) { fprintf(stderr, "Failed to read data\n"); goto error_free_buffer; } return 0; // Success // Error handling with cleanup error_free_buffer: free(res->buffer); res->buffer = NULL; error_close_file: fclose(res->f); res->f = NULL; error: return -1; } void cleanup(Resources *res) { if (res->buffer != NULL) { free(res->buffer); res->buffer = NULL; } if (res->f != NULL) { fclose(res->f); res->f = NULL; } } int main() { Resources res; // Try to read config if (read_config("nonexistent.dat", &res) != 0) { printf("Failed to read config\n"); cleanup(&res); return 1; } printf("Config read successfully\n"); cleanup(&res); return 0; }
Common patterns
Error path organization:
- Each allocation/open gets its own cleanup label
- Labels positioned in reverse order of allocation
- First error jumps to closest label
- Falls through to cleanup of prior resources
Naming convention:
error_resource_name:: clear what's cleaned uperror:: final fallback cleanup- Makes control flow obvious
Comparison with alternatives:
- No goto: deeply nested if/else, duplicate cleanup
- Goto: flat structure, cleanup centralized, efficient
- Exceptions (C++): automatic cleanup, but C lacks this
Resource management:
- Set pointers to NULL after freeing
- Check for NULL before freeing again
- Ensures idempotent cleanup (safe to call twice)
Scope alternatives (if available):
- RAII (C++): automatic cleanup when scope exits
- Defer (Go): execute function at end of scope
- Context managers (Python):
__exit__cleanup - C has no built-in; goto pattern is idiomatic
Best practices:
- Keep error paths simple and obvious
- Document what each label cleans up
- Test error paths thoroughly
- Don't use goto for other control flow (only errors)
Avoiding goto for non-error control:
gotofor error handling: accepted and idiomatic in Cgotofor loops/jumps: confusing, use loops instead- Linux kernel uses goto heavily for error paths
- Only structured goto (downward jumps) for errors
Error propagation:
- Return error code from function
- Caller decides whether to continue or unwind further
- Chain error returns up the call stack
- Each level cleans up its own resources
Modern C patterns:
- Use of
__attribute__((cleanup))(GCC) to auto-cleanup - Defer macros (simulating Go's defer)
- But goto error handling remains most portable
c-goto-error-handling.md · Last modified: by 127.0.0.1
