Skip to content

指针与内存操作

源:C Interop - Pointers

指针是 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 代码。