Calling C printf from Nim using FFI: Step‑by‑step guide
Learn how to call C's printf from Nim using the FFI pragma, compile with the correct linker flags, verify the output, and troubleshoot common linking issues.
31 Mar 2026, 15:30 UTC

Desired outcome
Compile a Nim program that invokes the C printf function and runs without linker errors, producing the expected printed output.
Prerequisites
- Nim compiler version 1.6 or newer (
nim --version). - A C compiler compatible with Nim’s backend (e.g.,
gccorclang). - The
nimblepackage manager (usually bundled with Nim). - A terminal with normal user privileges; no administrator rights are required for compilation.
Procedure
- Create the Nim source file. Save the following as
hello.nim:{#.pragma: cimport, <stdio.h>, "cdecl".#} proc printf*(cstring: cstring) {.importc, "printf", header: <stdio.h>.} when isMainModule: printf("Hello from C via Nim!\n")The
{.pragma: cimport, <stdio.h>, "cdecl".}pragma tells Nim to generate an#include <stdio.h>in the intermediate C file and to use thecdeclcalling convention, which matches the default forprintfon most platforms. - Compile the program. Run the following command in the directory containing
hello.nim:nim c --passC:-lc hello.nimThe
--passC:-lcflag forwards-lcto the C linker, ensuring the standard C library (which providesprintf) is linked. On Windows the flag can be omitted because the C runtime is linked by default, but including it does not cause harm. - Execute the resulting binary.
./hello # on Linux/macOS hello.exe # on Windows (if using the default name)
Expected checks
- The compilation step finishes with no warnings or errors.
- Running the executable prints exactly:
Hello from C via Nim!
- If you inspect the generated C intermediate file (
hello.c), you should see anexterndeclaration similar to:extern int printf(const char *);
Recovery options (troubleshooting)
- Linking fails with “undefined reference to `printf’”: verify that
-lcis being passed. You can see the full compile command by adding--verbosity:2. If the flag is missing, add it explicitly as shown. - Calling convention mismatch: ensure the pragma includes
"cdecl". On some platformsstdcallis used for certain Windows APIs; using the wrong convention leads to stack corruption. - Inspect generated C code: run
nim c --showPass:C hello.nimto dump the intermediate C file without assembling. Check that theprintfdeclaration matches the expected signature. - Debugging: compile with
-d:dangerto disable optimizations, then usegdborlldbto set a breakpoint atprintfand confirm control flows from Nim.
Limitations and safety considerations
- Nim’s garbage collector may relocate memory. Never pass a pointer to Nim‑managed data (e.g., a string or sequence) to C unless you pin it with
GC_refor allocate it withalloc0/deallocfrom thesystemmodule. - When using macros or compile‑time execution (
{.compileTime.}), ensure FFI declarations are outside macro expansion; otherwise the bindings could be stale after macro re‑evaluation. - Data type mismatches (e.g., passing a
intwhere C expectslong) cause undefined behavior. Match the C types exactly using Nim’scint,clong,cstring, etc.
Practical verification
- Run the executable and capture its output:
- Compare the content of
output.txtwith the expected lineHello from C via Nim!. Any deviation indicates a problem in the FFI setup. - Optionally, diff the generated C file against a known good template to ensure the
#includeandexternlines are present.
./hello > output.txt
cat output.txt
Rollback
Compiling creates the executable (hello or hello.exe) and intermediate files (hello.c, hello.o). To return to a clean state, delete them:
rm -f hello hello.exe hello.c hello.o
# on Windows: del hello.exe hello.c hello.o
This removes all artifacts produced by the procedure without affecting source files or the Nim toolchain.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.