The author, whose first systems language was Rust, notes that C did not have a primitive boolean type until the C23 standard; before that programmers had to include <stdbool.h> to get the macros true and false, which simply expand to the integer constants 1 and 0. This means that, unlike Rust’s built‑in bool, early C code treated booleans as ordinary integers, a fact that can surprise developers coming from languages where true and false are distinct literals. The post observes that even after C23 made booleans a true language primitive, older compilers still require the header for full compatibility.
C strings are null‑terminated rather than length‑prefixed. A C string is merely a pointer to its first character, and its length is determined by scanning for the zero byte (⟨0⟩) that marks the end. Consequently, every string must reserve an extra byte for that terminator, and forgetting to add it—or mis‑calculating the allocation—can cause out‑of‑bounds reads when functions like strcpy or strlen run past the intended buffer. The article illustrates the point with a manual string‑reversal routine that explicitly allocates len+1 bytes and writes the terminator at index len, showing the bookkeeping that Rust’s &str (which stores length alongside the pointer) avoids.

This image illustrates the prevalence of buffer overflow and out-of-bounds write vulnerabilities, which relate to C's manual memory management and lack of bounds checking discussed in the article.
C’s integer types are not fixed‑width; their exact size depends on the target platform. For example, long is 64 bits on Unix but 32 bits on Windows, while int is uniformly 32 bits on both but only guaranteed to be at least 16 bits by the standard. This variability makes portable code fragile unless developers use the fixed‑width aliases in <stdint.h> (uint32_t, int64_t, etc.). The post also highlights C’s minimal error handling: many functions signal failure by returning a sentinel value such as -1 or a null pointer, and errno may hold a thread‑local code for more detail, but there is no language mechanism forcing the caller to check the result. Forgetting to test the return value of malloc, for instance, leads to a segmentation fault when the null pointer is dereferenced, illustrating how C places the burden of safety on the programmer.