slop-stuff cheatsheets & experiments git

slop-stuff / Languages

C

Close to the metal — C17 first.

C17 pointers, ownership, strings, structs, UB hotspots, sanitizers, and C23 deltas.

languageC17pointersUBmalloc

Pin C17 (-std=c17) as the everyday baseline. C23 is a boxed delta below — not the 20-second path. Signed overflow is UB, not “wraps in practice.”

Quick reference

cc -std=c17 -Wall -Wextra -Wpedantic -g -O1 main.c -o main
# bugs:
cc -std=c17 -g -O1 -fno-omit-frame-pointer \
  -Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wformat=2 \
  -fsanitize=address,undefined,leak -fno-sanitize-recover=undefined main.c -o main
IdiomDo
Allocatep = malloc(n * sizeof *p); check n vs SIZE_MAX/sizeof *p
Growq = realloc(p, n); if (!q) /* p live */; else p = q;never p = realloc(p, n) alone
Zerocalloc(n, sz) — zeros and overflow-checks
Freefree(NULL) OK · set ptr NULL after if you keep the name
Print int64printf("%" PRId64 "\n", x); — not %ld on Win64
Bound stringsnprintf(dst, sizeof dst, "%s", src); · scanf("%63s", buf); with buf[64]
%d int   %u unsigned   %x hex   %c char   %s char*
%f double (printf)     %zu size_t   %p (void*)
%" PRId64 " / SCNd64   from <inttypes.h>

Types / sizes

ILP32LP64 (most *nix)LLP64 (Win64)
int323232
long326432
pointer326464

Rank: char < short < int < long < long long. Same-rank signed vs unsigned → usual arithmetic conversions: -1 < (size_t)10 is false.

char signedness is implementation-defined — use unsigned char for bytes / hashing.

Fixed widths: <stdint.h> + <inttypes.h> (int32_t, uint64_t, PRId64, SCNu64).

Pointers / decay

int x = 42;
int *p = &x;     // address-of
*p = 7;          // dereference
p + 1;           // next element (scaled)
int *a = arr;    // array → pointer decay
ExpressionMeaning
sizeof arrwhole array size (bytes)
sizeof ppointer width
void f(int a[n])parameter is int * — size lost
NITEMS(a) macroonly before decay

void * arithmetic is a GCC extension, not ISO C — cast to unsigned char * for byte walks.

Ownership / malloc

p = malloc(n * sizeof *p);
if (!p) { perror("malloc"); exit(1); }

q = realloc(p, n);
if (!q) { /* p still valid — handle OOM */ }
else p = q;

calloc(n, sz);           // prefer for zeroed arrays
free(p); p = NULL;       // hygiene, not a full UAF fix
  • Caller owns malloc / calloc / strdup results → caller free
  • strdup = POSIX + C23; Windows _strdup
  • C23: realloc(p, 0) is UB — don’t rely on free-or-null behavior

Strings

PreferAvoid
snprintf, strncpy_s (if available), width in scanfgets, unbounded strcpy/strcat, scanf("%s")
memcmp / length-aware APIsstrncpy as “safe strcpy” (no guaranteed NUL)
Explicit sizes everywhereAssuming char is unsigned

Missing NUL = classic OOB. Always track capacity.

Structs / unions / FAM

struct S { int n; char data[]; };          // flexible array member (C99+)
struct S *s = malloc(sizeof *s + n);
TopicRule
PaddingDon’t memcmp structs for equality blindly
Union active memberRead the last-written member (type-pun via memcpy is the safe habit)
Alignment_Alignas / alignas (C11+) when packing buffers

Preprocessor

#define NITEMS(a) (sizeof (a) / sizeof (a)[0])  // arrays only
#define MAX(a,b) ((a) > (b) ? (a) : (b))         // double-eval risk — prefer inline/static inline

#pragma once is not ISO — headers still need include guards for portability. Function-like macros: parens everywhere; watch multiple evaluation.

UB hotspots

UBReality
Signed overflowNot wrap — optimize as if it never happens
NULL derefNot guaranteed SIGSEGV
UAF / double-free / OOBSanitizers catch many; not all
Strict aliasingmemcpy between types; don’t lie through incompatible *
Unsequenced i = i++Don’t
Shift ≥ width / negativeUB
restrict lieOptimizer trusts you
VLA huge nStack bomb
Data raceSeparate problem — need TSan / atomics (don’t mix ASan+TSan+MSan)

Sanitize / warnings

cc -std=c17 -g -O1 -fno-omit-frame-pointer \
  -Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wformat=2 \
  -fsanitize=address,undefined,leak -fno-sanitize-recover=undefined

-Wconversion is noisy — keep it on purpose. No ASan+TSan+MSan combo in one binary.

C23 deltas

Teach: bool/true/false keywords · nullptr · typeof for macros · [[nodiscard]] · memset_explicit · strdup in ISO · ckd_add / checked math · realloc(p,0) is UB · K&R decls gone

Skip for this sheet: #embed · _BitInt · decimal FP · auto · constexpr · <stdbit.h> · free_sized

AI-slop kill-list

SlopReality
gets / unbounded strcpy/strcat/scanf("%s")Always bound
strncpy as safe strcpyMay omit NUL
sizeof(ptr) as lengthPointer width ≠ array length
void* arithmeticGCC ext; not ISO
malloc cast in CUnnecessary; can hide bugs
return &localDangling
%ld for int64_tBreaks on LLP64 — use PRId64
char for bytesSignedness impl-defined
p = realloc(p, n)Leak original on failure
while (!feof(f))Wrong loop shape
fflush(stdin)UB / nonportable
Macro MAX double-evalSide effects twice
#pragma once as ISOGuards still matter
Only -std=c23 on a C17 sheetBaseline is c17
INT_MAX+1 “wraps in practice”Signed overflow is UB
NULL deref “segfault”Not guaranteed
calloc == malloc+memsetcalloc also overflow-checks
p=NULL after free = full UAF fixOnly helps that name

Refs

  • cppreference C · C23
  • Working drafts: N3096 (last free pre-C23 WD — not the standard) · N3220 (post-C23 WD)
  • CERT C: INT32-C, MEM30-C, EXP33-C, ARR30-C, STR31-C, …
  • Clang ASan / UBSan docs · Open Group malloc / strdup