Claude Code Plugins

Community-maintained marketplace

Feedback

This skill should be used when working with C projects, "C11", "C17", "C23", "Makefile", "gcc", "clang", "valgrind", "getopt", or C language patterns. Provides comprehensive Modern C (C11-C23) patterns, memory management, and CLI development best practices.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name C Ecosystem
description This skill should be used when working with C projects, "C11", "C17", "C23", "Makefile", "gcc", "clang", "valgrind", "getopt", or C language patterns. Provides comprehensive Modern C (C11-C23) patterns, memory management, and CLI development best practices.
Provide comprehensive patterns for Modern C language (C11/C17/C23), memory management, toolchain configuration, and CLI tool development. ISO/IEC 9899:2011 - Major modernization with threading and type-generic features Type-generic selection for polymorphic macros Atomic types and operations for lock-free programming Thread-local storage duration Compile-time assertions Alignment specifier for variables and types Query alignment requirements Function that does not return Unnamed struct/union members ISO/IEC 9899:2018 - Bug fix release with no new features __STDC_VERSION__ updated to 201710L Defect reports resolved ATOMIC_VAR_INIT deprecated ISO/IEC 9899:2024 - Significant modernization with C++ alignment Null pointer constant with nullptr_t type Type inference operator Compile-time constant objects Type inference for variables 0b prefix for binary literals Single quote separator in numeric literals [[nodiscard]], [[maybe_unused]], [[deprecated]], [[fallthrough]], [[noreturn]] bool, true, false as keywords embed directive for binary resource inclusion Single argument form Fixed-width integer types from stdint.h Exact-width signed integers Exact-width unsigned integers Pointer-sized integers Unsigned size type for memory operations Signed pointer difference type #include <stdint.h> #include <stddef.h>

uint32_t compute_hash(const uint8_t *data, size_t len);

_Generic for type-based dispatch (C11) #define print_value(x) _Generic((x), \ int: print_int, \ double: print_double, \ char *: print_string, \ default: print_unknown \ )(x) Implement type-safe generic interfaces without void pointers Anonymous compound literals for in-place struct/array creation struct point { int x, y; }; void draw(struct point p);

draw((struct point){.x = 10, .y = 20});

int *arr = (int[]){1, 2, 3, 4, 5};

Named field initialization for structs and arrays struct config { int timeout; bool verbose; const char *name; };

struct config cfg = { .name = "myapp", .timeout = 30, .verbose = true, };

Do you need thread-safe operations? Use _Atomic for simple counters, pthreads for complex synchronization Single-threaded sequential execution Lock-free atomic operations (C11) #include <stdatomic.h>

_Atomic int counter = 0;

void increment(void) { atomic_fetch_add(&counter, 1); }

int get_count(void) { return atomic_load(&counter); }

Thread-local storage (C11) #include <threads.h>

_Thread_local int errno_local;

Common undefined behavior to avoid in C Null pointer dereference, use-after-free, buffer overflow, double-free Signed integer overflow, division by zero, shift beyond type width Strict aliasing violations, type punning without union Modifying variable twice between sequence points Use sanitizers (ASan, UBSan) during development to detect UB Overusing void\* for generic programming Use \_Generic macros or code generation Hardcoded numeric values without explanation Use named constants with define or enum Using unbounded string functions Use snprintf for strings. Note: strncpy does NOT null-terminate if source exceeds size Using sprintf without bounds checking Use snprintf with explicit buffer size Using gets() which has no bounds checking Use fgets() with explicit buffer size Using scanf with unbounded %s format Use %Ns with explicit width or fgets followed by sscanf Not checking malloc return value Always check for NULL after allocation Using user input as format string (printf(user_input)) Always use printf("%s", user_input) or puts()
What is the lifetime and access pattern of the memory? Use stack allocation or VLA (with size limit) Use malloc/free with clear ownership Use arena allocator Use pool allocator Standard malloc/calloc/realloc/free patterns #include <stdlib.h> #include <string.h>

char *duplicate_string(const char *src) { if (!src) return NULL;

size_t len = strlen(src) + 1;
char *dst = malloc(len);
if (!dst) return NULL;

memcpy(dst, src, len);
return dst;

} Always check malloc/calloc/realloc return value for NULL Use calloc for zero-initialized memory After realloc, use the returned pointer (original may be invalid) Set pointer to NULL after free to prevent use-after-free

Bulk allocation with single-point deallocation typedef struct { char *base; size_t size; size_t offset; } Arena;

Arena arena_create(size_t size) { Arena a = {0}; a.base = malloc(size); if (a.base) a.size = size; return a; }

void *arena_alloc(Arena *a, size_t bytes) { size_t aligned = (bytes + 7) & ~7; // 8-byte alignment if (a->offset + aligned > a->size) return NULL; void *ptr = a->base + a->offset; a->offset += aligned; return ptr; }

void arena_reset(Arena *a) { a->offset = 0; }

void arena_destroy(Arena *a) { free(a->base); *a = (Arena){0}; } Parsing, compilers, request handling - many allocations freed together

Fixed-size object allocation with O(1) alloc/free typedef struct PoolBlock { struct PoolBlock *next; } PoolBlock;

typedef struct { PoolBlock *free_list; char *memory; size_t object_size; size_t capacity; } Pool;

Pool pool_create(size_t object_size, size_t count) { Pool p = {0}; size_t size = object_size > sizeof(PoolBlock) ? object_size : sizeof(PoolBlock); p.memory = malloc(size * count); if (!p.memory) return p;

p.object_size = size;
p.capacity = count;

// Build free list
for (size_t i = 0; i &lt; count; i++) {
    PoolBlock *block = (PoolBlock *)(p.memory + i * size);
    block-&gt;next = p.free_list;
    p.free_list = block;
}
return p;

}

void *pool_alloc(Pool *p) { if (!p->free_list) return NULL; PoolBlock *block = p->free_list; p->free_list = block->next; return block; }

void pool_free(Pool *p, void *ptr) { PoolBlock *block = ptr; block->next = p->free_list; p->free_list = block; }

void pool_destroy(Pool *p) { free(p->memory); *p = (Pool){0}; } Game entities, network connections, fixed-size records

Resource cleanup with goto for error handling int process_file(const char *path) { int result = -1; FILE *fp = NULL; char *buffer = NULL;
fp = fopen(path, "r");
if (!fp) goto cleanup;

buffer = malloc(BUFFER_SIZE);
if (!buffer) goto cleanup;

// ... process file ...

result = 0;  // Success

cleanup: free(buffer); if (fp) fclose(fp); return result; } Single cleanup point prevents resource leaks on error paths

Forgetting to free allocated memory Use Valgrind/ASan, establish clear ownership rules Freeing the same pointer twice Set pointer to NULL after free, use ownership tracking Accessing memory after it has been freed Clear pointers after free, use ASan for detection Writing beyond allocated buffer bounds Always track buffer sizes, use bounded functions
What complexity of argument parsing is needed? Use getopt() Use getopt_long() Use argp (GNU extension) POSIX standard option parsing #include <unistd.h> #include <stdio.h> #include <stdlib.h>

int main(int argc, char *argv[]) { int verbose = 0; const char *output = NULL; int opt;

while ((opt = getopt(argc, argv, "vo:h")) != -1) {
    switch (opt) {
    case 'v':
        verbose = 1;
        break;
    case 'o':
        output = optarg;
        break;
    case 'h':
        printf("Usage: %s [-v] [-o output] [file...]\n", argv[0]);
        return 0;
    default:
        fprintf(stderr, "Usage: %s [-v] [-o output] [file...]\n", argv[0]);
        return 1;
    }
}

// Remaining arguments: argv[optind] to argv[argc-1]
for (int i = optind; i &lt; argc; i++) {
    printf("Processing: %s\n", argv[i]);
}

return 0;

}

GNU extension for long options #include <getopt.h> #include <stdio.h> #include <stdlib.h>

static struct option long_options[] = { {"verbose", no_argument, NULL, 'v'}, {"output", required_argument, NULL, 'o'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0 } };

int main(int argc, char *argv[]) { int verbose = 0; const char *output = NULL; int opt;

while ((opt = getopt_long(argc, argv, "vo:h", long_options, NULL)) != -1) {
    switch (opt) {
    case 'v': verbose = 1; break;
    case 'o': output = optarg; break;
    case 'h':
        printf("Usage: %s [--verbose] [--output FILE] [file...]\n", argv[0]);
        return 0;
    default:
        return 1;
    }
}

return 0;

}

Standard exit code conventions Successful execution General error Command line usage error Command found but not executable Command not found Terminated by signal N #include <stdlib.h> #include <sysexits.h> // EX_USAGE, EX_DATAERR, etc.

int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: %s <file>\n", argv[0]); return EX_USAGE; // 64 }

FILE *fp = fopen(argv[1], "r");
if (!fp) {
    perror(argv[1]);
    return EX_NOINPUT;  // 66
}

// ...

return EXIT_SUCCESS;

}

Graceful signal handling for clean shutdown #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <stdatomic.h>

static atomic_int running = 1;

static void handle_signal(int sig) { (void)sig; running = 0; }

int main(void) { struct sigaction sa = {0}; sa.sa_handler = handle_signal; sigemptyset(&sa.sa_mask); sa.sa_flags = 0;

sigaction(SIGINT, &amp;sa, NULL);
sigaction(SIGTERM, &amp;sa, NULL);

while (running) {
    // Main loop work
}

printf("Shutting down gracefully...\n");
return 0;

} Use sigaction() instead of signal() for portability Keep signal handlers minimal - set flag only Use volatile sig_atomic_t or atomic types for signal flags

Consistent error message format #include <errno.h> #include <stdarg.h> #include <stdio.h> #include <string.h>

static const char *progname = "myapp";

void set_progname(const char *argv0) { const char *p = strrchr(argv0, '/'); progname = p ? p + 1 : argv0; }

void error(const char *fmt, ...) { fprintf(stderr, "%s: ", progname); va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); }

void error_errno(const char *fmt, ...) { int saved_errno = errno; fprintf(stderr, "%s: ", progname); va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, ": %s\n", strerror(saved_errno)); }

GNU Compiler Collection Enable C11 standard (or c17, c23) Comprehensive warnings Treat warnings as errors Warn on variable shadowing Warn on implicit conversions Require function prototypes Static analysis (GCC 10+) LLVM C compiler Enable C11 standard (or c17, c23) Comprehensive warnings Treat warnings as errors All warnings (use selectively) Memory error detection (buffer overflow, use-after-free) -fsanitize=address -fno-omit-frame-pointer gcc -fsanitize=address -fno-omit-frame-pointer -g -o myapp myapp.c Undefined behavior detection -fsanitize=undefined gcc -fsanitize=undefined -g -o myapp myapp.c Data race detection -fsanitize=thread Cannot be combined with AddressSanitizer Uninitialized memory read detection (Clang only) -fsanitize=memory

<staticanalysis> Clang-based linter and static analyzer clang-tidy src/.c -- -std=c11 <filereference>.clang-tidy Checks: > -, bugprone-, clang-analyzer-, misc-, performance-, readability-*, -readability-identifier-length

WarningsAsErrors: '*'

Static analysis tool for C/C++ cppcheck --enable=all --error-exitcode=1 src/ Runtime memory error detection valgrind --leak-check=full --show-leak-kinds=all ./myapp Memory error detection (default) Thread error detection Cache profiling Call graph profiling What testing style do you prefer? Use Check or Unity Use cmocka Use Unity (smallest footprint) Unit testing framework for C #include <check.h>

START_TEST(test_addition) { ck_assert_int_eq(1 + 1, 2); } END_TEST

Suite *math_suite(void) { Suite *s = suite_create("Math"); TCase *tc = tcase_create("Core"); tcase_add_test(tc, test_addition); suite_add_tcase(s, tc); return s; }

int main(void) { Suite *s = math_suite(); SRunner *sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); int failed = srunner_ntests_failed(sr); srunner_free(sr); return failed ? EXIT_FAILURE : EXIT_SUCCESS; }

What is your project's complexity and portability needs? Use Make Use CMake or Meson Use Meson Basic Makefile for C projects CC := gcc CFLAGS := -std=c11 -Wall -Wextra -Wpedantic -g LDFLAGS := LDLIBS :=

SRCS := $(wildcard src/*.c) OBJS := $(SRCS:.c=.o) TARGET := myapp

.PHONY: all clean

all: $(TARGET)

$(TARGET): $(OBJS) $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)

%.o: %.c $(CC) $(CFLAGS) -c -o $@ $<

clean: rm -f $(OBJS) $(TARGET)

Modern CMake for C projects cmake_minimum_required(VERSION 3.20) project(myapp VERSION 1.0.0 LANGUAGES C)

set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF)

add_executable(myapp src/main.c src/utils.c) target_include_directories(myapp PRIVATE include)

target_compile_options(myapp PRIVATE $<$<C_COMPILER_ID:GNU,Clang>: -Wall -Wextra -Wpedantic -Werror > ) For detailed CMake patterns, see cplusplus-ecosystem skill

Meson build for C projects # meson.build project('myapp', 'c', version: '1.0.0', default_options: [ 'c_std=c11', 'warning_level=3', 'werror=true', ] )

src = files('src/main.c', 'src/utils.c') inc = include_directories('include')

executable('myapp', src, include_directories: inc)

Use Context7 MCP for up-to-date C documentation resolve-library-id libraryName="cppreference" get-library-docs context7CompatibleLibraryID="/websites/cppreference_com" topic="stdatomic.h" get-library-docs context7CompatibleLibraryID="/websites/cppreference_com" topic="malloc" Always check return values of malloc/calloc/realloc Enable -Wall -Wextra -Werror for all builds Run with AddressSanitizer during development Use Valgrind before release Use fixed-width integer types from stdint.h Prefer snprintf over sprintf for buffer safety Use designated initializers for struct clarity Document ownership semantics in function comments Use static for file-local functions and variables Prefer const for read-only parameters Use enum for related constants instead of define Include what you use - minimize header dependencies Understand C code requirements 1. Check for existing code patterns and conventions 2. Identify memory ownership requirements 3. Review header dependencies Write safe, portable C code 1. Use appropriate C standard (C11 minimum recommended) 2. Follow memory management patterns 3. Handle all error conditions Verify C code correctness 1. Compile with warnings enabled 2. Run with sanitizers 3. Test with Valgrind Compiler warning about unused variable Fix warning, maintain clean build Valgrind reports minor memory leak Track down and fix leak, verify with Valgrind AddressSanitizer detects buffer overflow Stop, fix immediately - this is a security issue Use-after-free or double-free detected Block operation, require immediate fix and review Check all allocation return values Use bounded string functions (snprintf, strncpy) Enable compiler warnings Run sanitizers during development Using gets(), sprintf(), or other unsafe functions Raw pointer arithmetic without bounds checking Implicit type conversions that may lose data Global mutable state when possible Memory architecture, data structure design C implementation with proper memory management Buffer overflow detection, input validation Cache optimization, memory layout, profiling with Valgrind Navigate C codebases and header hierarchies C documentation via /websites/cppreference_com CMake patterns and clang-tidy configuration Debugging with Valgrind, GDB, and sanitizers