# 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. ```c // compile: gcc -o goto_error goto_error.c // run: ./goto_error // description: error handling with goto for cleanup #include #include #include 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 up - `error:`: 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**: - `goto` for error handling: accepted and idiomatic in C - `goto` for 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