类型映射与转换
理解 C 和 Kotlin 之间的类型映射规则是 Native 互操作的核心。Kotlin/Native 提供了一套完整的类型系统来安全地操作 C 数据结构。
基本类型映射表
整数类型完整映射
| C 类型 | Kotlin 类型 | 字节数 | 说明 |
|---|---|---|---|
signed char | Byte | 1 | 有符号 8 位 |
unsigned char | UByte | 1 | 无符号 8 位 |
short | Short | 2 | 有符号 16 位 |
unsigned short | UShort | 2 | 无符号 16 位 |
int | Int | 4 | 有符号 32 位 |
unsigned int | UInt | 4 | 无符号 32 位 |
long | Long* | 4/8 | 平台相关 |
unsigned long | ULong* | 4/8 | 平台相关 |
long long | Long | 8 | 有符号 64 位 |
unsigned long long | ULong | 8 | 无符号 64 位 |
size_t | ULong | 平台相关 | 无符号大小类型 |
ptrdiff_t | Long | 平台相关 | 有符号差值类型 |
intptr_t | Long | 平台相关 | 指针大小整数 |
平台差异
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 类型 | 字节数 | 精度 |
|---|---|---|---|
float | Float | 4 | 单精度 |
double | Double | 8 | 双精度 |
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 类型 |
|---|---|
Int | IntVar |
Long | LongVar |
Float | FloatVar |
Double | DoubleVar |
Byte | ByteVar |
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 开发的基础。