0Pricing
Kotlin Academy · Lesson

C Interop with cinterop and .def Files

Call C libraries from Kotlin/Native using cinterop tool and definition files.

C Interop with cinterop and .def Files is a free Kotlin Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Kotlin Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Why C Interop?

Kotlin/Native can call C libraries directly — no JNI wrappers, no bridging layer. This enables using platform system libraries, hardware drivers, or any C-compatible library from Kotlin code compiled to native binaries.

The .def File

A .def file describes the C library to import. It specifies headers, library name, and optional compiler/linker flags. Place it in src/nativeInterop/cinterop/:

# src/nativeInterop/cinterop/libcurl.def
headers = curl/curl.h
headerFilter = curl/**
linkerOpts.linux = -lcurl
linkerOpts.macos = -lcurl

Registering the Interop in Gradle

Reference the .def file in the target's compilations block:

kotlin {
    linuxX64 {
        compilations["main"].cinterops {
            val libcurl by creating {
                defFile(project.file("src/nativeInterop/cinterop/libcurl.def"))
            }
        }
    }
}

Running cinterop

Run ./gradlew cinteropLibcurlLinuxX64 to generate a .klib from the C headers. The generated Kotlin bindings appear in build/cinterop/ and are automatically added to the compilation classpath.

Using C Functions in Kotlin

After generation, import the package declared in the .def file and call C functions as Kotlin functions. Pointer types are represented as CPointer:

import libcurl.*
import kotlinx.cinterop.*

fun fetchUrl(url: String): String = memScoped {
    val handle = curl_easy_init() ?: error("curl init failed")
    curl_easy_setopt(handle, CURLOPT_URL, url.cstr.ptr)
    curl_easy_perform(handle)
    curl_easy_cleanup(handle)
    "done"
}

memScoped: Stack-Allocated Memory

memScoped { } creates a memory scope where native allocations (alloc(), allocArray()) are automatically freed when the block exits. Always use it for temporary C memory to avoid leaks.

memScoped {
    val value = alloc<IntVar>()
    value.value = 42
    println(value.value)  // 42
    // freed automatically at end of memScoped
}

Pinning Kotlin Objects for C

To pass a Kotlin object's address to a C function (e.g., a callback), pin it with StableRef or Pinned to prevent the GC from moving it:

val stableRef = StableRef.create(myKotlinObject)
try {
    c_function_expecting_user_data(stableRef.asCPointer())
} finally {
    stableRef.dispose()
}

C Strings and Kotlin Strings

Convert between Kotlin String and C const char* using:

  • "hello".cstr → CValues, valid inside memScoped
  • cPointer.toKString() → Kotlin String from a CPointer<ByteVar>
memScoped {
    val cStr = "Hello, C!".cstr.ptr   // CPointer<ByteVar>
    val back = cStr.toKString()         // "Hello, C!"
}

Struct Access

C structs become Kotlin classes with properties. Access struct fields using the dot operator. Allocate structs with alloc() inside a memScoped { }:

memScoped {
    val addr = alloc<sockaddr_in>()
    addr.sin_family = AF_INET.convert()
    addr.sin_port = htons(8080u).convert()
}

Embedding Inline C Code

For short C snippets, use the cCode block in the .def file to write inline C that gets compiled with the binding:

# In .def file
headers = stdio.h
---
// Inline C code after ---
static inline int add(int a, int b) { return a + b; }

Debugging C Interop Issues

Common issues: missing headers (check headerFilter), missing linker flags (add linkerOpts), mismatched types (check generated .klib API). Enable verbose cinterop with -verbose in the def file or Gradle task configuration.

Quick Check

What is the purpose of memScoped { } in Kotlin/Native C interop?

Recap: C Interop with cinterop and .def Files

Key takeaways:

  • .def file specifies headers, linker options, and optional inline C
  • Register in Gradle via cinterops { val name by creating { defFile(...) } }
  • Run the cinterop<Name><Target> Gradle task to generate bindings
  • Use memScoped { } for temporary native allocations
  • Pin long-lived objects with StableRef; convert strings with .cstr / .toKString()

Frequently asked questions

Is the “C Interop with cinterop and .def Files” lesson free?

Yes — the full text of “C Interop with cinterop and .def Files” is free to read here on the web, and the Kotlin Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Kotlin Academy course, upgrade to CoddyKit PRO.

What will I learn in “C Interop with cinterop and .def Files”?

Call C libraries from Kotlin/Native using cinterop tool and definition files. You practise Kotlin Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Kotlin Academy?

No prior experience is required. Kotlin Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “C Interop with cinterop and .def Files” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Kotlin Academy lesson?

Yes. Every Kotlin Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Kotlin/Native Overview: Targets, Toolchain & Compilation
  2. Memory Management in Kotlin/Native: The New MM
  3. C Interop with cinterop and .def Files
  4. Kotlin/WASM: Compiling to WebAssembly for the Browser
← Back to Kotlin Academy