Skip to content

类型映射与转换

源:C Interop Type Mapping

理解 C 和 Kotlin 之间的类型映射规则是 Native 互操作的核心。Kotlin/Native 提供了一套完整的类型系统来安全地操作 C 数据结构。

基本类型映射表

整数类型完整映射

C 类型Kotlin 类型字节数说明
signed charByte1有符号 8 位
unsigned charUByte1无符号 8 位
shortShort2有符号 16 位
unsigned shortUShort2无符号 16 位
intInt4有符号 32 位
unsigned intUInt4无符号 32 位
longLong*4/8平台相关
unsigned longULong*4/8平台相关
long longLong8有符号 64 位
unsigned long longULong8无符号 64 位
size_tULong平台相关无符号大小类型
ptrdiff_tLong平台相关有符号差值类型
intptr_tLong平台相关指针大小整数

平台差异

long 类型在不同平台有不同大小:

  • Linux x64 / macOS: 8 字节
  • Windows x64: 4 字节
  • iOS ARM64: 8 字节
kotlin
@OptIn(ExperimentalForeignApi::class)
fun demonstrateIntegerTypes() {
    // C: void process(int a, unsigned int b, long long c)
    process(42, 100u, 9223372036854775807L)
    
    // 显式转换
    val kotlinInt: Int = 42
    val cUInt: UInt = kotlinInt.toUInt()
    process_unsigned(cUInt)
}

浮点类型

C 类型Kotlin 类型字节数精度
floatFloat4单精度
doubleDouble8双精度
kotlin
@OptIn(ExperimentalForeignApi::class)
fun calculateArea(radius: Float): Double {
    // C: double calculate_circle_area(float r)
    return calculate_circle_area(radius)
}

布尔类型

C 类型Kotlin 类型
_Bool (C99)Boolean
bool (stdbool.h)Boolean
kotlin
@OptIn(ExperimentalForeignApi::class)
fun checkCondition(): Boolean {
    // C: bool is_valid(int value)
    return is_valid(42)
}

指针类型

CPointer<T>

所有 C 指针都映射为 CPointer<T>,其中 T 是变量类型:

C 类型Kotlin 类型
int*CPointer<IntVar>
unsigned char*CPointer<UByteVar>
double*CPointer<DoubleVar>
void*CPointer<*>COpaquePointer
const char*CPointer<ByteVar> (字符串时自动转换)
kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlinx.cinterop.*

fun pointerExamples() {
    // C: int* allocate_int()
    val intPtr: CPointer<IntVar> = allocate_int()
    
    // 读取值
    val value: Int = intPtr.pointed.value
    
    // 写入值
    intPtr.pointed.value = 100
    
    // 数组访问
    intPtr[0] = 42
    intPtr[1] = 43
}

Var 类型

CPointer<T> 中的 T 必须是 Var 类型:

基础类型Var 类型
IntIntVar
LongLongVar
FloatFloatVar
DoubleDoubleVar
ByteByteVar
kotlin
@OptIn(ExperimentalForeignApi::class)
memScoped {
    // 分配单个变量
    val intVar: IntVar = alloc<IntVar>()
    intVar.value = 42
    
    // 分配数组
    val intArray: CPointer<IntVar> = allocArray<IntVar>(10)
    for (i in 0 until 10) {
        intArray[i] = i * 2
    }
}

二级指针

C 的二级指针(指针的指针):

kotlin
@OptIn(ExperimentalForeignApi::class)
fun doublePointerExample() {
    // C: void get_buffer(char** out_buffer)
    memScoped {
        val bufferPtr = allocPointerTo<CPointerVar<ByteVar>>()
        get_buffer(bufferPtr.ptr)
        
        val buffer: CPointer<ByteVar>? = bufferPtr.value
        buffer?.let {
            println(it.toKString())
        }
    }
}

函数指针

函数指针映射为 CPointer<CFunction<...>>

kotlin
@OptIn(ExperimentalForeignApi::class)
// C: typedef int (*Comparator)(int a, int b);
typealias Comparator = CPointer<CFunction<(Int, Int) -> Int>>

fun sortWithComparator(array: CPointer<IntVar>, size: Int, comp: Comparator) {
    // C: void sort_array(int* arr, int size, Comparator comp)
    sort_array(array, size, comp)
}

// 创建函数指针
fun createComparator(): Comparator {
    return staticCFunction { a: Int, b: Int -> a - b }
}

数组类型

C 数组 -> Kotlin

C 数组映射为 CPointer<T>,无法直接区分数组和单个指针:

kotlin
@OptIn(ExperimentalForeignApi::class)
fun processArray(array: CPointer<IntVar>, size: Int) {
    // C: void process(int* array, int size)
    for (i in 0 until size) {
        val element = array[i]
        println(element)
    }
}

固定大小数组

C 结构体中的固定大小数组:

c
// C 代码
struct ImageData {
    int width;
    int height;
    unsigned char pixels[1024];  // 固定大小数组
};
kotlin
@OptIn(ExperimentalForeignApi::class)
fun accessFixedArray(image: CPointer<ImageData>) {
    val data = image.pointed
    
    // 访问固定大小数组
    for (i in 0 until 1024) {
        val pixel = data.pixels[i]
        println(pixel)
    }
}

结构体类型

基础结构体

C 结构体映射为 Kotlin 类型,字段通过 .pointed 访问:

c
struct Point {
    double x;
    double y;
};
kotlin
@OptIn(ExperimentalForeignApi::class)
fun usePoint(ptr: CPointer<Point>) {
    val point = ptr.pointed
    
    // 读取字段
    val x: Double = point.x
    val y: Double = point.y
    
    // 写入字段
    point.x = 10.0
    point.y = 20.0
}

嵌套结构体

c
// C 代码
struct Rectangle {
    struct Point topLeft;
    struct Point bottomRight;
};
kotlin
@OptIn(ExperimentalForeignApi::class)
fun useRectangle(rect: CPointer<Rectangle>) {
    val r = rect.pointed
    
    // 访问嵌套结构体
    val x1 = r.topLeft.x
    val y1 = r.topLeft.y
    val x2 = r.bottomRight.x
    val y2 = r.bottomRight.y
}

创建结构体

kotlin
@OptIn(ExperimentalForeignApi::class)
fun createPointScoped(): CPointer<Point> {
    return memScoped {
        val point = alloc<Point>()
        point.x = 10.0
        point.y = 20.0
        // 警告:返回的指针在离开作用域后无效
        point.ptr
    }
}
kotlin
@OptIn(ExperimentalForeignApi::class)
fun createPointHeap(): CPointer<Point> {
    val point = nativeHeap.alloc<Point>()
    point.x = 10.0
    point.y = 20.0
    return point.ptr
    // 必须手动调用 nativeHeap.free(point)
}
kotlin
@OptIn(ExperimentalForeignApi::class)
fun createPointC(): CPointer<Point>? {
    // C: Point* create_point(double x, double y)
    return create_point(10.0, 20.0)
    // 由 C 侧管理内存
}

枚举类型

C 枚举映射

C 枚举可以映射为 Kotlin 枚举或整数:

c
// C 代码
enum Color {
    RED = 0,
    GREEN = 1,
    BLUE = 2
};
kotlin
@OptIn(ExperimentalForeignApi::class)
fun useEnum() {
    // 作为枚举值
    val color: Color = Color.RED
    
    // 作为整数
    val colorValue: Int = Color.RED.value
    
    // C: void set_color(enum Color c)
    set_color(Color.BLUE)
}

位标志枚举

C 的位标志枚举:

c
// C 代码
enum FileMode {
    READ = 0x01,
    WRITE = 0x02,
    EXECUTE = 0x04
};
kotlin
@OptIn(ExperimentalForeignApi::class)
fun useFlags() {
    // 组合位标志
    val mode = FileMode.READ.value or FileMode.WRITE.value
    
    // C: void open_file(const char* path, int mode)
    open_file("test.txt", mode)
}

联合体类型

C 的 union 映射为 Kotlin 结构体,但只有一个字段有效:

c
// C 代码
union Value {
    int i;
    float f;
    double d;
};
kotlin
@OptIn(ExperimentalForeignApi::class)
fun useUnion() {
    memScoped {
        val value = alloc<Value>()
        
        // 写入 int
        value.i = 42
        
        // 读取 float(未定义行为!)
        val f = value.f
    }
}

DANGER

访问未设置的联合体字段是未定义行为,需要在应用层跟踪当前类型。

typedef 类型别名

C 的 typedef 映射为 Kotlin 的 typealias

c
// C 代码
typedef unsigned long size_t;
typedef int (*CompareFunc)(void*, void*);
kotlin
@OptIn(ExperimentalForeignApi::class)
// 自动生成
// typealias size_t = ULong
// typealias CompareFunc = CPointer<CFunction<(COpaquePointer?, COpaquePointer?) -> Int>>

fun useTypedefs(size: size_t, compare: CompareFunc) {
    // 直接使用类型别名
}

字符串转换

const char* → String

自动转换或手动转换:

kotlin
@OptIn(ExperimentalForeignApi::class)
fun getString() {
    // C: const char* get_name()
    
    // 自动转换(函数返回值)
    val name: String = get_name()
    
    // 手动转换(指针参数)
    val namePtr: CPointer<ByteVar> = get_name_ptr()
    val name2: String = namePtr.toKString()
}

String → const char*

使用 .cstr 扩展:

kotlin
@OptIn(ExperimentalForeignApi::class)
fun sendString(message: String) {
    memScoped {
        // C: void send(const char* msg)
        send(message.cstr.ptr)
    }
    // 指针在此处失效
}
kotlin
@OptIn(ExperimentalForeignApi::class)
class StringHolder(message: String) {
    private val cString = message.cstr.getPointer(nativeHeap)
    
    fun getCString(): CPointer<ByteVar> = cString
    
    fun release() {
        nativeHeap.free(cString)
    }
}

CValue 与 CPointer

CValue<T>

CValue<T> 表示栈上的 C 值(值语义):

kotlin
@OptIn(ExperimentalForeignApi::class)
fun useCValue() {
    // 创建栈上的 Point
    val pointValue: CValue<Point> = cValue {
        x = 10.0
        y = 20.0
    }
    
    // 使用 CValue
    memScoped {
        // C: double calculate(Point p)  // 按值传递
        val result = calculate(pointValue)
    }
}

转换关系

kotlin
@OptIn(ExperimentalForeignApi::class)
memScoped {
    // CPointer → CValue
    val ptr: CPointer<Point> = alloc<Point>().ptr
    val value: CValue<Point> = ptr.pointed.readValue()
    
    // CValue → CPointer
    val newPtr: CPointer<Point> = value.ptr
}

复杂类型转换示例

多维数组

kotlin
@OptIn(ExperimentalForeignApi::class)
fun create2DArray(rows: Int, cols: Int): CPointer<CPointerVar<IntVar>> {
    val array = nativeHeap.allocArray<CPointerVar<IntVar>>(rows)
    for (i in 0 until rows) {
        array[i] = nativeHeap.allocArray<IntVar>(cols)
        for (j in 0 until cols) {
            array[i]!![j] = i * cols + j
        }
    }
    return array
}

fun free2DArray(array: CPointer<CPointerVar<IntVar>>, rows: Int) {
    for (i in 0 until rows) {
        nativeHeap.free(array[i])
    }
    nativeHeap.free(array)
}

结构体数组

kotlin
@OptIn(ExperimentalForeignApi::class)
fun createPointArray(count: Int): CPointer<Point> {
    val points = nativeHeap.allocArray<Point>(count)
    for (i in 0 until count) {
        points[i].x = i.toDouble()
        points[i].y = i * 2.0
    }
    return points
}

回调函数转换

kotlin
@OptIn(ExperimentalForeignApi::class)
// Kotlin 函数
val kotlinComparator: (Int, Int) -> Int = { a, b -> a - b }

// 转换为 C 函数指针
val cComparator: CPointer<CFunction<(Int, Int) -> Int>> = 
    staticCFunction { a, b -> a - b }

// 使用
fun sortArray(array: CPointer<IntVar>, size: Int) {
    // C: void qsort(void* arr, size_t n, size_t size, int(*comp)(const void*, const void*))
    qsort(array.reinterpret(), size.toULong(), sizeOf<IntVar>().toULong(), cComparator.reinterpret())
}

类型安全实践

使用 inline class 包装

kotlin
@OptIn(ExperimentalForeignApi::class)
@JvmInline
value class FileDescriptor(val fd: Int) {
    fun isValid(): Boolean = fd >= 0
}

fun openFile(path: String): FileDescriptor {
    // C: int open(const char* path, int flags)
    val fd = open(path, O_RDONLY)
    return FileDescriptor(fd)
}

智能类型转换

kotlin
@OptIn(ExperimentalForeignApi::class)
fun safePointerAccess(ptr: CPointer<IntVar>?) {
    if (ptr != null) {
        // 自动智能转换为非空类型
        val value = ptr.pointed.value
        println(value)
    }
}

掌握类型映射规则,能够让你在 C 和 Kotlin 之间安全、高效地转换数据。理解指针、结构体、枚举等复杂类型的映射,是进行深度 Native 开发的基础。