How to read C type declarations (2003)

C’s type declaration syntax—especially for pointers, arrays, and function pointers—is widely criticized as unreadable, yet some argue it’s internally consistent once you understand its “declaration follows usage” model. Commenters compare C’s approach to languages like Rust, Go, Ada, Zig, and C++, debating whether more algebraic or colon-based type syntax would improve clarity or simply shift complexity elsewhere. Many advocate practical workarounds such as `typedef`s, helper tools like cdecl.org, and avoiding overly complex types in real-world code, while also revisiting deeper design issues like array decay and null pointers.

Overall view of C type syntax

  • Many find C’s declaration syntax “horrible”, especially for complex pointer/array/function types.
  • Others argue it’s actually simple and elegant once understood: “declaration follows usage” and there is almost no separate “type syntax”.
  • There’s broad agreement that extreme declarations (e.g. nested pointers to arrays of function pointers) are unreadable and rarely justified in real code.

How to read/write declarations

  • Recommended mental model: treat a declaration as an expression centered on the identifier, then apply operators (*, [], ()) with their usual precedence.
  • Several posters say the “clockwise/reverse spiral rule” is misleading or outright wrong; better to understand the actual grammar.
  • A strong theme: break complex types into named pieces using typedef/aliases; if a structure is used multiple places, it deserves its own name.

Function pointers, arrays, and pointers-to-arrays

  • Function pointers are widely cited as the hardest part; many admit still looking up the syntax after years.
  • Common pattern: first typedef the function type, then use Func *p instead of raw int (*p)(int).
  • Pointers to arrays are considered both rare and a readability smell; array-decay semantics are called one of C’s biggest design mistakes.

Comparisons to other languages

  • Rust: some say Rust syntax is harder to read and has accreted complexity; others find Rust’s tree-like type syntax much clearer than C’s mixed prefix/postfix style.
  • Go: its C-inspired declarations are debated; many wish it had name: type with colons, noting this would help both readability and parsing, especially with generics.
  • Ada, Pascal, Zig, D, C#, Nim, Odin, C# spans, and C++ templates/aliases are mentioned as having more algebraic or wordy type syntax that often reads more clearly.

Grammar, parsing, and language design

  • C’s context-sensitive grammar and lexer hack are criticized as objectively poor design, though some say practical parsers are still simple.
  • Multiple variable declarations with commas (especially with pointers) are widely viewed as a historical mistake; many advocate “one variable per line”.

Tools and workarounds

  • cdecl (and its web version) is repeatedly recommended for translating between C “gibberish” and English.
  • Other resources and habits: online function pointer guides, writing small helper macros (_Ptr, _Array), and avoiding clever one-liners in favor of clarity.