指针与内存操作
指针是 C 互操作的核心概念。Kotlin/Native 提供了类型安全的指针操作API,本文深入讲解如何在 Kotlin 中安全、高效地使用指针。
CPointer<T> 基础
指针类型系统
kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlinx.cinterop.*
//单值指针
val intPtr: CPointer<IntVar> = nativeHeap.alloc<IntVar>().ptr
val doublePtr: CPointer<DoubleVar> = nativeHeap.alloc<DoubleVar>().ptr
// 数组指针
val arrayPtr: CPointer<IntVar> = nativeHeap.allocArray<IntVar>(10)
// void* 指针
val voidPtr: COpaquePointer = intPtr.rawValue
// 函数指针
val funcPtr: CPointer<CFunction<(Int) -> Int>> = staticCFunction { x -> x * 2 }指针运算
kotlin
@OptIn(ExperimentalForeignApi::class)
fun pointerArithmetic() {
val array = nativeHeap.allocArray<IntVar>(10)
// 访问元素
array[0] = 10
array[5] = 50
// 指针偏移
val ptr5 = array + 5 // 等价于 &array[5]
println(ptr5.pointed.value) // 50
// 指针差值
val diff = ptr5 - array // 5
// 指针比较
if (ptr5 > array) {
println("ptr5 在 array 之后")
}
nativeHeap.free(array)
}内存分配
memScoped - 栈式分配
kotlin
@OptIn(ExperimentalForeignApi::class)
fun stackAllocation() {
memScoped {
// 分配单个值
val intVar = alloc<IntVar>()
intVar.value = 42
// 分配数组
val buffer = allocArray<ByteVar>(1024)
// 分配结构体
val point = alloc<Point>()
point.x = 10.0
point.y = 20.0
// 使用 cstr 分配字符串
val str = "Hello".cstr
// memScoped 结束时自动释放所有分配
}
}nativeHeap - 堆分配
kotlin
@OptIn(ExperimentalForeignApi::class)
class NativeBuffer(size: Int) {
private val buffer: CPointer<ByteVar> = nativeHeap.allocArray(size)
private val size: Int = size
fun set(index: Int, value: Byte) {
if (index in 0 until size) {
buffer[index] = value
}
}
fun get(index: Int): Byte {
return if (index in 0 until size) buffer[index] else 0
}
fun release() {
nativeHeap.free(buffer)
}
}allocArrayOf - 初始化数组
kotlin
@OptIn(ExperimentalForeignApi::class)
memScoped {
// 使用初始值创建数组
val numbers = allocArrayOf(1, 2, 3, 4, 5)
// 字符串数组
val strings = allocArrayOf("Hello".cstr, "World".cstr)
// 字节数组
val bytes = allocArrayOf<ByteVar>(0x01, 0x02, 0x03)
}指针解引用
pointed 属性
kotlin
@OptIn(ExperimentalForeignApi::class)
fun dereferencePointer() {
val ptr = nativeHeap.alloc<IntVar>().ptr
// 写入值
ptr.pointed.value = 100
// 读取值
val value = ptr.pointed.value // 100
nativeHeap.free(ptr)
}[] 操作符
kotlin
@OptIn(ExperimentalForeignApi::class)
fun arrayAccess() {
val array = nativeHeap.allocArray<IntVar>(5)
// 使用 [] 访问
for (i in 0 until 5) {
array[i] = i * 10
}
// 等价于
for (i in 0 until 5) {
(array + i).pointed.value = i * 10
}
nativeHeap.free(array)
}指针转换
类型转换
kotlin
@OptIn(ExperimentalForeignApi::class)
fun pointerCasting() {
val intPtr = nativeHeap.alloc<IntVar>().ptr
// 转换为 void*
val voidPtr: COpaquePointer = intPtr.rawValue
// 从 void* 转换回来
val intPtr2: CPointer<IntVar> = voidPtr.reinterpret()
// 类型重新解释
val bytePtr: CPointer<ByteVar> = intPtr.reinterpret()
nativeHeap.free(intPtr)
}Long ↔ CPointer
kotlin
@OptIn(ExperimentalForeignApi::class)
fun ptrToLong() {
val ptr = nativeHeap.alloc<IntVar>().ptr
// CPointer → Long
val address: Long = ptr.rawValue.toLong()
// Long → CPointer
val ptr2: CPointer<IntVar> = address.toCPointer()!!
nativeHeap.free(ptr)
}字符串与指针
Kotlin String → C String
kotlin
@OptIn(ExperimentalForeignApi::class)
// 临时字符串
memScoped {
val cStr: CPointer<ByteVar> = "Hello".cstr.ptr
// 使用 cStr
}
// 持久化字符串
val cStr = "Hello".cstr.getPointer(nativeHeap)
// 稍后释放
nativeHeap.free(cStr)C String → Kotlin String
kotlin
@OptIn(ExperimentalForeignApi::class)
fun cStringToKotlin(cStr: CPointer<ByteVar>?): String? {
return cStr?.toKString()
}结构体指针
访问结构体字段
kotlin
@OptIn(ExperimentalForeignApi::class)
// C 结构体
// struct Point {
// double x;
// double y;
// };
fun manipulateStruct() {
val point = nativeHeap.alloc<Point>()
// 设置字段
point.x = 10.0
point.y = 20.0
// 获取指针
val ptr: CPointer<Point> = point.ptr
// 通过指针访问
ptr.pointed.x = 15.0
println("(${ptr.pointed.x}, ${ptr.pointed.y})")
nativeHeap.free(point)
}结构体数组
kotlin
@OptIn(ExperimentalForeignApi::class)
fun structArray() {
val points = nativeHeap.allocArray<Point>(3)
points[0].x = 1.0
points[0].y = 2.0
points[1].x = 3.0
points[1].y = 4.0
points[2].x = 5.0
points[2].y = 6.0
nativeHeap.free(points)
}函数指针
创建函数指针
kotlin
@OptIn(ExperimentalForeignApi::class)
// C 原型: int (*comparator)(int, int)
typealias Comparator = CPointer<CFunction<(Int, Int) -> Int>>
val ascending: Comparator = staticCFunction { a, b -> a - b }
val descending: Comparator = staticCFunction { a, b -> b - a }调用函数指针
kotlin
@OptIn(ExperimentalForeignApi::class)
fun callFunctionPointer(func: Comparator) {
// 直接调用
val result = func.invoke(10, 20)
println(result)
}内存拷贝
使用 memcpy
kotlin
@OptIn(ExperimentalForeignApi::class)
import platform.posix.memcpy
fun copyMemory() {
val src = nativeHeap.allocArray<IntVar>(5)
val dst = nativeHeap.allocArray<IntVar>(5)
// 初始化源数组
for (i in 0 until 5) {
src[i] = i * 10
}
// 拷贝内存
memcpy(dst, src, (5 * sizeOf<IntVar>()).toULong())
// 验证
for (i in 0 until 5) {
println("dst[$i] = ${dst[i]}")
}
nativeHeap.free(src)
nativeHeap.free(dst)
}Kotlin 扩展函数
kotlin
@OptIn(ExperimentalForeignApi::class)
fun CPointer<ByteVar>.copyTo(dst: CPointer<ByteVar>, size: Int) {
platform.posix.memcpy(dst, this, size.toULong())
}
// 使用
val src = nativeHeap.allocArray<ByteVar>(100)
val dst = nativeHeap.allocArray<ByteVar>(100)
src.copyTo(dst, 100)安全实践
空指针检查
kotlin
@OptIn(ExperimentalForeignApi::class)
fun safeAccess(ptr: CPointer<IntVar>?) {
ptr?.let {
val value = it.pointed.value
println(value)
} ?: println("Null pointer")
}边界检查
kotlin
@OptIn(ExperimentalForeignApi::class)
class SafeArray(private val size: Int) {
private val data = nativeHeap.allocArray<IntVar>(size)
operator fun get(index: Int): Int {
require(index in 0 until size) { "Index out of bounds" }
return data[index]
}
operator fun set(index: Int, value: Int) {
require(index in 0 until size) { "Index out of bounds" }
data[index] = value
}
fun release() {
nativeHeap.free(data)
}
}RAII 模式
kotlin
@OptIn(ExperimentalForeignApi::class)
class NativeResource<T : CVariable>(private val allocator: NativePlacement) {
private var ptr: CPointer<T>? = null
inline fun <reified R : T> alloc(): CPointer<R> {
val p = allocator.alloc<R>().ptr
ptr = p.reinterpret()
return p
}
fun free() {
ptr?.let { allocator.free(it) }
ptr = null
}
}
// 使用
val resource = NativeResource<IntVar>(nativeHeap)
val ptr = resource.alloc<IntVar>()
ptr.pointed.value = 42
resource.free()掌握指针操作是 C 互操作的基础。Kotlin/Native 提供了类型安全的 API,同时保留了 C 的灵活性和性能。正确使用指针能够实现高效的 Native 代码。