C (C23)
This assessment applies the coupling account — coupling as access to a shared resource, and the coupling floor [1], [2] — and the knowledge-embodiment theorem [3]; the language is assessed against its official reference [4].
1 Assessment
This page assesses C in five parts: core coupling — what the language’s own constructs force; ecosystem coupling — what the surrounding platform, tooling, and community cost; engineering readiness — whether the artifact can be reproduced, verified, observed, worked in quickly, connected to other languages, and governed openly; the profile — the unscored facts a score cannot express; and the scoring detail — the mechanism marks and the arithmetic behind every score. A ✅ bullet marks a construct that helps. A ⚠️ or ❌ bullet marks a cost; the scoring model does not price every cost named here.
Core coupling 25/100 · Ecosystem coupling 73/100 · Engineering readiness 65/100
1.1 Core coupling
1.1.1 Type
- ✅ Structs separate data: Pure data, no methods
- ✅ Function pointers enable polymorphism: Can pass behavior as data
- ❌ No encapsulation: Everything is public
- ❌ No generics: Void pointers couple to type casting
- ✅ No inheritance: Can’t create coupled hierarchies
- ❌ Header files couple: Includes create compilation dependencies
- ❌ Macros couple: Preprocessor doesn’t respect scope
- ❌ No namespaces: Global namespace pollution
1.1.2 Construction
- ✅ Explicit memory management: Allocation separate from initialization
- ✅ Designated initializers: Can initialize specific fields
- ❌ No constructors: Manual initialization couples to usage
- ❌ Global state common: Static variables couple modules
1.1.3 Memory
- ✅ Manual control enables precise memory strategy: Can separate memory management from logic
- ✅ Explicit allocation/deallocation: malloc/free make lifetime visible
- ❌ No safety: Use-after-free, double-free, memory leaks all possible
- ❌ No RAII: Must manually track and free resources
- ✅ Predictable: No GC pauses, deterministic performance
- ❌ Pointer arithmetic couples to memory layout: Buffer overruns couple to hardware, with no abstraction over memory
- ✅ Custom allocators: Can decouple allocation strategy (arena, pool, etc.)
- ❌ Undefined behavior on errors: Buffer overruns, null derefs, and other memory-safety violations are UB
1.1.4 Error
- ✅ Explicit error codes: Return values make errors visible
- ❌ No enforcement: Can ignore return values
- ❌ errno couples: Global error state couples functions
- ❌ NULL everywhere: Pointer errors couple all code
1.1.5 Concurrency
- ❌ pthreads manual: All synchronization explicit
- ❌ Shared mutable state default: No protection
- ❌ Data races are undefined behavior: No prevention
- ⚠️ Explicit is predictable: At least coupling is visible
1.1.6 Platform
- ✅ No platform owner: The language is standardised by ISO/IEC and implemented independently by GCC, Clang, and MSVC, so no single vendor controls it
- ✅ No required platform runtime: A conforming program depends only on the C standard library, not on a vendor runtime
- ✅ Every mainstream platform has a C compiler: Linux, macOS, Windows, and embedded targets all have conforming implementations, and operating systems expose a C ABI to other languages
- ✅ No single-implementation dependence: Source compiles under more than one compiler, so the loss of one implementation does not strand the code
1.1.7 Trade-offs (not coupling)
- Power vs. Safety: Complete control couples to complete responsibility for safety
- Simplicity vs. Safety: No abstractions means manual discipline required to control coupling
- Performance vs. Correctness: Optimization couples to unsafe patterns (buffer tricks, pointer arithmetic)
- Portability vs. Platform Access: Platform-specific code couples to hardware/OS
- Manual Memory Management: Must couple resource acquisition to every usage site
- Undefined Behavior Everywhere: Memory errors couple entire program to runtime environment
- Header Dependencies: Changes to one header couple to all files that include it (transitively)
1.2 Ecosystem coupling
1.2.1 Performance
- ✅ Maximum performance: No abstraction overhead
- ✅ No GC: Deterministic, predictable
- ✅ Full control: Manual everything
- ✅ Predictable: What you write is what runs
- ✅ Minimal runtime: Runs anywhere
1.2.2 Ecosystem
- ✅ Ubiquitous: Available on every platform
- ⚠️ Build systems: Make, autotools, CMake fragmentation
- ✅ Mature tooling: GCC, Clang, debuggers
- ⚠️ No package manager: Manual dependency management
- ✅ Stable ABI on a given platform: A platform’s C ABI is long-stable, so binaries built by ABI-compatible compilers interoperate on that platform (the C standard itself defines no ABI, and compatibility is not guaranteed across compilers or targets)
1.2.3 Evolution
- ✅ Extremely stable: C89/C99/C11/C17/C23 all backward compatible
- ✅ No breaking changes: Code from 1980s still compiles
- ✅ Conservative: Changes are minimal
- ⚠️ Slow evolution: New features take decades
1.2.4 Domain
- ✅ Excellent for systems: OS kernels, drivers
- ✅ Great for embedded: Minimal footprint
- ✅ Strong for performance: No overhead
- ✅ Good for portability: Runs everywhere
- ⚠️ Not for application dev: Too low-level
1.2.5 Learning
- ✅ Simple language: Small, learnable
- ❌ Hard to master: Undefined behavior, pointers
- ✅ Lots of resources: K&R, tutorials
- ❌ Many footguns: Easy to shoot yourself
1.2.6 Syntax
- ✅ Simple, direct: Minimal syntax
- ❌ No abstractions: Manual everything
- ❌ Pointer syntax:
int *(*fp)(void)confusing - ✅ Explicit: Clear what’s happening
1.2.7 Testing
- ⚠️ Testing frameworks: Check, Unity, cmocka
- ✅ Excellent debuggers: Gdb, lldb
- ❌ Manual memory tracking: Valgrind needed
- ⚠️ UB makes debugging hard: Anything can happen
1.2.8 Security
- ❌ Memory unsafe: All memory bugs possible
- ❌ Buffer overflows: No bounds checking
- ❌ Undefined behavior: Security vulnerabilities
- ❌ No protection: Manual everything
1.3 Engineering readiness
1.3.1 Reproducibility
- ❌ Builds are not byte-reproducible by default.
- ❌ Dependencies resolve without a lock by default.
1.3.2 Supply chain
- ⚠️ Published packages carry no signed provenance by default.
1.3.3 Observability
- ⚠️ No stable tracing and metrics standard.
- ⚠️ The standard library has no structured logging.
1.3.4 Feedback
- ✅ No charged mechanism in this dimension.
1.3.5 Interop
- ✅ No charged mechanism in this dimension.
1.3.6 Governance
- ✅ No charged mechanism in this dimension.
1.4 Profile
Unscored, and kept separate from every score on purpose. The profile records what a mechanism price cannot express: what a running program costs, how well a coding model and an agent work in the language, and whether a safety-critical path exists.
1.4.1 Energy and cost
What this axis asks: what does a running program cost in energy and time?
1.4.2 AI and agent readiness
What this axis asks: how well does a coding model and an agent work in this language?
1.4.3 Safety certification
What this axis asks: is there a certified or standards-qualified path to a safety-critical system?
- ✅ A standards-qualified toolchain or an established safety standard covers this language.
1.5 Scoring detail
1.5.1 Core coupling Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Type | T1 |
dynamic no static check | ✅ | −15 |
T2 |
weak static | ✅ | −8 | |
T3 |
dynamic/duck dispatch | — | 0 | |
T4 |
no sum types | — | 0 | |
T5 |
no generics | ✅ | −4 | |
T6 |
no namespaces | ✅ | −8 | |
T7 |
type erasure/reflection | — | 0 | |
T8 |
operator overloading | ✅ | −4 | |
T9 |
inheritance required for polymorphism | ✅ | −4 | |
T10 |
no null-safety | ✅ | −8 | |
T11 |
no module boundary | ✅ | −8 | |
T12 |
no effect tracking | ✅ | −8 | |
T13 |
async/sync colouring | — | 0 | |
T14 |
unchecked foreign boundary | ✅ | −4 | |
| Type subtotal | −15 (−71) | |||
| Construction | C1 |
global mutable state by default | ✅ | −15 |
C2 |
no immutability by default | ✅ | −4 | |
C3 |
constructors coupled to allocation | ✅ | −4 | |
C4 |
no constructor/validation | ✅ | −4 | |
| Construction subtotal | −15 (−27) | |||
| Memory | M1 |
manual deallocation | ✅ | −15 |
M2 |
GC non-deterministic pauses | — | 0 | |
M3 |
no RAII | ✅ | −4 | |
M4 |
no ownership model | ✅ | −8 | |
| Memory subtotal | −15 (−27) | |||
| Error | Er1 |
no structured path | ✅ | −15 |
Er2 |
unchecked exceptions only | — | 0 | |
Er3 |
errors ignored/non-exhaustive | ✅ | −4 | |
Er4 |
no composable result type | ✅ | −4 | |
| Error subtotal | −15 (−23) | |||
| Concurrency | K1 |
GIL/no parallel | — | 0 |
K2 |
shared mutable state default | ✅ | −8 | |
K3 |
manual sync no race detection | ✅ | −8 | |
K4 |
no structured concurrency | ✅ | −4 | |
K5 |
no primitives | — | 0 | |
| Concurrency subtotal | −15 (−20) | |||
| Platform | P1 |
cannot function outside one vendor/domain | — | 0 |
P2 |
single-vendor but other uses | — | 0 | |
P3 |
legacy/discontinued platform | — | 0 | |
| Platform subtotal | 0 |
Core Dimensions score: 25/100 — total cost = −75 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
1.5.2 Ecosystem coupling Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Performance | R1 |
interpreted no AOT/JIT | — | 0 |
R2 |
GC pauses | — | 0 | |
R3 |
JIT warmup | — | 0 | |
| Performance subtotal | 0 | |||
| Ecosystem | Ec1 |
no package manager | ✅ | −4 |
Ec2 |
fragmented tooling | ✅ | −8 | |
Ec3 |
small/niche/discontinued | — | 0 | |
Ec4 |
proprietary | — | 0 | |
Ec5 |
platform-locked tooling | — | 0 | |
Ec6 |
monoculture | — | 0 | |
Ec7 |
legacy burden | — | 0 | |
| Ecosystem subtotal | −12 | |||
| Evolution | V1 |
breaking changes | — | 0 |
V2 |
frozen | — | 0 | |
V3 |
discontinued | — | 0 | |
V4 |
glacial standards | — | 0 | |
| Evolution subtotal | 0 | |||
| Domain | D1 |
domain/vendor lock-in | — | 0 |
D2 |
poor domain fit | — | 0 | |
D3 |
framework forces architecture | — | 0 | |
| Domain subtotal | 0 | |||
| Learning | L1 |
steep | — | 0 |
L2 |
large surface/multi-paradigm | — | 0 | |
L3 |
sparse docs | — | 0 | |
| Learning subtotal | 0 | |||
| Syntax | S1 |
verbose | — | 0 |
S2 |
inconsistent | — | 0 | |
S3 |
punctuation-heavy | — | 0 | |
| Syntax subtotal | 0 | |||
| Testing | G1 |
weak tooling | — | 0 |
G2 |
poor debugging | — | 0 | |
G3 |
runtime-only errors | — | 0 | |
| Testing subtotal | 0 | |||
| Security | Q1 |
memory-unsafe | ✅ | −15 |
Q2 |
dynamic/weak typing risk | — | 0 | |
Q3 |
known vulnerability/injection | — | 0 | |
| Security subtotal | −15 |
Meta Dimensions score: 73/100 — total cost = −27 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
1.5.3 Engineering readiness Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Reproducibility | Y1 |
build not reproducible | ✅ | −8 |
Y2 |
dependencies not pinned | ✅ | −8 | |
| Reproducibility subtotal | −15 (−16) | |||
| Supply chain | Y3 |
no signed provenance | ✅ | −8 |
Y4 |
registry without namespacing | — | 0 | |
| Supply chain subtotal | −8 | |||
| Observability | Y5 |
no stable tracing standard | ✅ | −8 |
Y6 |
no structured logging | ✅ | −4 | |
| Observability subtotal | −12 | |||
| Feedback | Y7 |
slow build | — | 0 |
Y8 |
no fast in-editor analysis | — | 0 | |
| Feedback subtotal | 0 | |||
| Interop | Y9 |
no foreign-function interface | — | 0 |
Y10 |
no portable target | — | 0 | |
| Interop subtotal | 0 | |||
| Governance | Y11 |
single-vendor governance | — | 0 |
Y12 |
closed specification | — | 0 | |
| Governance subtotal | 0 |
Readiness Dimensions score: 65/100 — total cost = −35 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
Verdict: C provides no coupling prevention mechanisms but also imposes few forced coupling constructs — the language itself is neutral. With discipline, developers can separate concerns through function pointers, explicit error codes, and manual memory management. Without discipline, globals, macros, and undefined behavior couple everything. Main insight: coupling in C is entirely up to the programmer. The same language can produce extremely low or extremely high coupling depending on how it is used.
The formal development. The definitions, theorems, and proofs behind this page are published in:
- On the Nature of Coupling: All Coupling as Shared Resource Access — Zenodo, 2026 — doi:10.5281/zenodo.21925122
- Coupling as a Consequence of Cohesion: Decomposition, the Coupling Floor, and Domain-Imposed Coupling — Zenodo, 2026 — doi:10.5281/zenodo.21925201
- On the Nature of Cohesion: Cohesion as Correctness Relative to a Partitioning Rule — Zenodo, 2026 — doi:10.5281/zenodo.21703193