Why D Interoperates with C So Easily
D was designed with C ABI compatibility as a first-class goal: an extern(C) function uses the platform's standard C calling convention and skips D's name mangling entirely, so it links directly against object code compiled from real C sources -- static .a/.lib archives or shared .so/.dll libraries -- with zero wrapper or shim code required.
Cricket analogy: When Virat Kohli moves from IPL colors straight into national team whites, no retraining is needed because both use the same bat, pitch, and scoring rules; D's extern(C) functions plug straight into existing C object code the same way, because both share the same calling convention.
Declaring C Functions and extern(C)
To call a C function from D, you declare its prototype with extern(C) linkage matching the original C signature exactly, then link against the compiled library using dmd/ldc2's -L flags or dub's 'libs' setting; the core.stdc.* modules already provide extern(C) declarations for the C standard library, so you rarely need to hand-write raw declarations for common functions like printf, malloc, or strlen.
Cricket analogy: Before facing a fast bowler like Jasprit Bumrah, a batsman studies his exact release point and seam position to predict the delivery; declaring an extern(C) prototype in D is that same study, writing down the C function's exact signature so the linker knows precisely what to expect.
// mathinterop.d
import core.stdc.stdio : printf;
extern(C) double sqrt(double x); // matches C's <math.h> sqrt
void main() {
double result = sqrt(2.0);
printf("sqrt(2.0) = %f\n", result);
}
// Compile and link against libm:
// dmd mathinterop.d -L-lmMatching Struct Layout Between D and C
When exchanging structs across the interop boundary, D's default struct layout already follows the platform's C ABI rules for field ordering, size, and alignment, so a D struct with the same field types and order as a C struct is binary-compatible without any special attribute; if the C side used #pragma pack(1) or __attribute__((packed)), you must add D's align(1) to the struct definition or the field offsets will silently diverge and corrupt data read across the boundary.
Cricket analogy: A pitch curator measures grass length and soil compaction to match the exact spec used at another venue so a day-night Test plays consistently; matching D struct layout to a C struct's packing is that same precise spec-matching, since a mismatched pitch shifts every subsequent measurement.
D's garbage collector does not know about memory allocated by C's malloc, and C code has no idea about D's GC-managed heap. If you pass a GC-allocated D array or class instance into a C function that stores the pointer for later use, the GC may collect or move that memory once no D-visible reference remains, corrupting the C side. Use core.memory.GC.addRoot for anything that must outlive the collector's visibility, or allocate with core.stdc.stdlib.malloc instead, and always call GC.removeRoot when done.
Calling D Functions from C and Building Shared Libraries
To expose a D function to C, mark it extern(C) so the compiler emits an unmangled symbol name, compile it into a static or shared library (or use dub's 'dynamicLibrary' targetType), and declare a matching prototype in the C header; the D runtime must be initialized first via rt_init()/rt_term() -- and thread_attachThis()/thread_detachThis() for callbacks arriving on C-created threads -- whenever any D-side GC, exceptions, or class features are touched, since C has no notion of the D runtime.
Cricket analogy: When a franchise signs an overseas player like Pat Cummins for the IPL, he must first register with the league and clear visa formalities before taking the field; a D function called from C needs the runtime initialized with rt_init() before it can safely take the field.
The dpp tool and druntime's core.stdc.* modules can auto-translate C headers into extern(C) D declarations, saving you from hand-transcribing large libraries; for C++ headers, D's extern(C++) linkage additionally preserves name mangling and class layout, letting you call C++ methods more directly than through a plain C shim.
- extern(C) linkage removes D's name mangling so functions link directly against compiled C object code.
- core.stdc.* modules already declare most C standard library functions; avoid re-declaring them by hand.
- D struct layout matches C's by default; use align() to mirror any #pragma pack in the C struct.
- The D garbage collector cannot see C-allocated memory, and C code can't see D's GC heap -- root anything shared.
- Exposing a D function to C requires extern(C) linkage plus a matching C prototype in a header.
- A C host calling into GC- or exception-touching D code must call rt_init() (and rt_term() at shutdown) first.
- extern(C++) exists separately from extern(C) for interoperating with C++ symbols and class layouts.
Practice what you learned
1. What does marking a D function extern(C) primarily change?
2. Where do most ready-made D bindings for the C standard library live?
3. Why must you sometimes add align(1) to a D struct used in C interop?
4. What risk does D's garbage collector pose to memory shared with C code?
5. What must a C program typically do before calling a D function that touches the GC or exceptions?
Was this page helpful?
You May Also Like
BetterC and Systems Programming
The -betterC compiler switch strips D down to a GC-free, runtime-minimal subset ideal for embedded, kernel, and other systems-programming contexts where a full runtime isn't an option.
DUB Package Management
DUB is D's official build tool and package manager -- it resolves dependencies, drives builds across configurations, and publishes reusable packages to the DUB registry.
Testing D Code
D has unit testing built directly into the language with unittest blocks, plus contract programming (in/out/invariant) and assert for runtime correctness checks.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics