0Pricing
Kotlin Academy · 课时

使用 cinterop 和 .def 文件进行 C 互操作

使用 cinterop 工具和定义文件从 Kotlin/Native 调用 C 库。

使用 cinterop 和 .def 文件进行 C 互操作 是 CoddyKit 上的免费 Kotlin Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Kotlin Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Kotlin Academy 课程共包含 4 节课。

为什么需要 C 互操作

Kotlin/Native 可以直接调用 C 库——无需 JNI 包装器,也无需桥接层。这样,使用 Kotlin 编译为原生二进制文件的代码,就能调用平台系统库、硬件驱动程序或任何兼容 C 的库。

.def 文件

.def 文件描述要导入的 C 库。它指定头文件、库名称以及可选的编译器/链接器标志。将它放在 src/nativeInterop/cinterop/ 中:

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

在 Gradle 中注册互操作

在目标的 compilations 块中引用 .def 文件:

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

运行 cinterop

运行 ./gradlew cinteropLibcurlLinuxX64,根据 C 头文件生成 .klib。生成的 Kotlin 绑定会出现在 build/cinterop/ 中,并自动添加到编译类路径中。

在 Kotlin 中使用 C 函数

生成完成后,导入 .def 文件中声明的包,并像调用 Kotlin 函数一样调用 C 函数。指针类型表示为 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:栈分配的内存

memScoped { } 会创建一个内存作用域,在代码块退出时自动释放本地内存分配(alloc()、allocArray())。请始终将它用于临时 C 内存,以避免内存泄漏。

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

为 C 固定 Kotlin 对象

要将 Kotlin 对象的地址传递给 C 函数(例如回调函数),请使用 StableRef 或 Pinned 固定该对象,以防垃圾回收器移动它:

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

C 字符串与 Kotlin 字符串

使用以下方式在 Kotlin String 和 C const char* 之间进行转换:

  • "hello".cstr → CValues,仅在 memScoped 内有效
  • cPointer.toKString() → 从 CPointer<ByteVar> 转换得到 Kotlin String
memScoped {
    val cStr = "Hello, C!".cstr.ptr   // CPointer<ByteVar>
    val back = cStr.toKString()         // "Hello, C!"
}

访问结构体

C 结构体会转换为带属性的 Kotlin 类。使用点号运算符访问结构体字段。在 memScoped { } 内使用 alloc() 分配结构体:

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

嵌入 inline C 代码

对于简短的 C 代码片段,可以在 .def 文件中使用 cCode 块编写 inline C 代码,该代码会与绑定一起编译:

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

调试 C 互操作问题

常见问题包括:缺少头文件(检查 headerFilter)、缺少链接器标志(添加 linkerOpts)、类型不匹配(检查生成的 .klib API)。在 def 文件或 Gradle 任务配置中使用 -verbose 启用详细的 cinterop 日志。

快速检查

memScoped { } 在 Kotlin/Native C 互操作中的作用是什么?

回顾:使用 cinterop 和 .def 文件进行 C 互操作

要点:

  • .def 文件指定头文件、链接器选项和可选的 inline C 代码
  • 通过 cinterops { val name by creating { defFile(...) } } 在 Gradle 中注册
  • 运行 cinterop<Name><Target> Gradle 任务以生成绑定
  • 使用 memScoped { } 进行临时原生内存分配
  • 使用 StableRef 固定长期存在的对象;使用 .cstr / .toKString() 转换字符串

常见问题解答

「使用 cinterop 和 .def 文件进行 C 互操作」课时是免费的吗?

是的 — 「使用 cinterop 和 .def 文件进行 C 互操作」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Kotlin Academy 课程的其余内容,请升级到 CoddyKit PRO。 Kotlin Academy 课程共包含 4 节课。

「使用 cinterop 和 .def 文件进行 C 互操作」这节课中我会学到什么?

使用 cinterop 工具和定义文件从 Kotlin/Native 调用 C 库。 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Kotlin Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Kotlin Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「使用 cinterop 和 .def 文件进行 C 互操作」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Kotlin Academy 课中编写并运行代码吗?

能。每节 Kotlin Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. Kotlin/Native 概览:目标、工具链与编译
  2. Kotlin/Native 中的内存管理:新版 MM
  3. 使用 cinterop 和 .def 文件进行 C 互操作
  4. Kotlin/WASM:为浏览器编译为 WebAssembly
← 返回 Kotlin Academy