目录
[1.1 什么是FFI](#1.1 什么是FFI)
[1.2 仓颉FFI架构设计](#1.2 仓颉FFI架构设计)
[1.3 安全边界设计](#1.3 安全边界设计)
[2.1 基本类型映射表](#2.1 基本类型映射表)
[2.2 基本类型转换示例](#2.2 基本类型转换示例)
[2.3 字符串类型转换](#2.3 字符串类型转换)
[2.4 结构体类型映射](#2.4 结构体类型映射)
[2.5 数组和指针转换](#2.5 数组和指针转换)
[3.1 集成SQLite数据库](#3.1 集成SQLite数据库)
[3.2 集成JSON解析库(cJSON)](#3.2 集成JSON解析库(cJSON))
[4.1 基准测试框架](#4.1 基准测试框架)
[4.2 FFI调用开销分析](#4.2 FFI调用开销分析)
[5.1 RAII模式实现](#5.1 RAII模式实现)
[5.2 智能指针实现](#5.2 智能指针实现)
[5.3 内存泄漏检测](#5.3 内存泄漏检测)
[6.1 回调函数处理](#6.1 回调函数处理)
[6.2 可变参数函数](#6.2 可变参数函数)
[6.3 函数指针表](#6.3 函数指针表)
[7.1 集成libpng](#7.1 集成libpng)
[8.1 FFI使用原则](#8.1 FFI使用原则)
[8.2 性能优化清单](#8.2 性能优化清单)
[8.3 常见陷阱](#8.3 常见陷阱)
摘要
Foreign Function Interface (FFI) 是连接不同编程语言的桥梁,使开发者能够复用现有的C/C++库生态。仓颉语言提供了强大而安全的FFI机制,支持与C/C++代码的无缝互操作。本文深入探讨仓颉FFI的实现原理、类型映射规则、内存管理策略以及性能优化技巧,通过丰富的实战案例帮助开发者构建高性能的混合语言应用,充分利用HarmonyOS原生能力和第三方库生态。
一、FFI原理与架构
1.1 什么是FFI

FFI的核心作用:
| 功能 | 说明 | 优势 |
|---|---|---|
| 代码复用 | 调用现有C/C++库 | 避免重复开发 |
| 性能优化 | 关键路径使用原生代码 | 突破语言性能瓶颈 |
| 系统集成 | 访问操作系统API | 实现底层功能 |
| 生态互通 | 利用成熟库生态 | 加速开发进度 |
1.2 仓颉FFI架构设计
// ========== FFI模块组织结构 ==========
/*
project/
├── src/
│ └── main.cj # 仓颉主代码
├── native/
│ ├── include/
│ │ └── mylib.h # C头文件
│ └── src/
│ └── mylib.c # C实现
├── ffi/
│ └── bindings.cj # FFI绑定
└── build.cj # 构建配置
*/
FFI调用流程:

1.3 安全边界设计
// ========== FFI安全检查机制 ==========
import std.ffi.*
// 不安全的FFI调用需要显式标记
@unsafe
extern func c_malloc(size: UInt64): RawPointer
@unsafe
extern func c_free(ptr: RawPointer)
// 安全的包装函数
func safeAllocate(size: UInt64): Result<ManagedPointer, Error> {
if size == 0 {
return Err(Error("Invalid allocation size"))
}
if size > MAX_ALLOCATION_SIZE {
return Err(Error("Allocation size too large"))
}
// 在unsafe块中调用
let rawPtr = unsafe {
c_malloc(size)
}
if rawPtr.isNull() {
return Err(Error("Allocation failed"))
}
// 包装为托管指针
return Ok(ManagedPointer(rawPtr, size))
}
安全边界层级:

二、类型映射与转换
2.1 基本类型映射表
// ========== 仓颉 ⟷ C 类型对应关系 ==========
// 整数类型
/*
┌─────────────┬─────────────┬──────────┐
│ 仓颉类型 │ C类型 │ 大小 │
├─────────────┼─────────────┼──────────┤
│ Int8 │ int8_t │ 1字节 │
│ UInt8 │ uint8_t │ 1字节 │
│ Int16 │ int16_t │ 2字节 │
│ UInt16 │ uint16_t │ 2字节 │
│ Int32 │ int32_t │ 4字节 │
│ UInt32 │ uint32_t │ 4字节 │
│ Int64 │ int64_t │ 8字节 │
│ UInt64 │ uint64_t │ 8字节 │
└─────────────┴─────────────┴──────────┘
*/
// 浮点类型
/*
┌─────────────┬─────────────┬──────────┐
│ Float32 │ float │ 4字节 │
│ Float64 │ double │ 8字节 │
└─────────────┴─────────────┴──────────┘
*/
// 布尔类型
/*
┌─────────────┬─────────────┬──────────┐
│ Bool │ bool/_Bool │ 1字节 │
└─────────────┴─────────────┴──────────┘
*/
// 指针类型
/*
┌─────────────┬─────────────┬──────────┐
│ RawPointer │ void* │ 8字节 │
│ &T │ T* │ 8字节 │
│ &mut T │ T* │ 8字节 │
└─────────────┴─────────────┴──────────┘
*/
2.2 基本类型转换示例
import std.ffi.*
// ========== C库函数声明 ==========
// C代码: int add(int a, int b);
@extern("C")
extern func c_add(a: Int32, b: Int32): Int32
// C代码: double sqrt(double x);
@extern("C", name: "sqrt")
extern func c_sqrt(x: Float64): Float64
// C代码: void* malloc(size_t size);
@extern("C")
@unsafe
extern func c_malloc(size: UInt64): RawPointer
// ========== 仓颉包装函数 ==========
func add(a: Int32, b: Int32): Int32 {
return c_add(a, b)
}
func squareRoot(x: Float64): Result<Float64, Error> {
if x < 0.0 {
return Err(Error("Cannot compute square root of negative number"))
}
return Ok(c_sqrt(x))
}
func testBasicTypes() {
// 整数运算
let sum = add(10, 20)
println("10 + 20 = ${sum}") // 30
// 浮点运算
match squareRoot(16.0) {
case Ok(result) => println("sqrt(16) = ${result}") // 4.0
case Err(e) => println("Error: ${e.message}")
}
}
2.3 字符串类型转换
import std.ffi.*
// ========== C字符串函数 ==========
// C代码: size_t strlen(const char* str);
@extern("C")
extern func c_strlen(str: CString): UInt64
// C代码: char* strcpy(char* dest, const char* src);
@extern("C")
@unsafe
extern func c_strcpy(dest: CString, src: CString): CString
// C代码: int strcmp(const char* s1, const char* s2);
@extern("C")
extern func c_strcmp(s1: CString, s2: CString): Int32
// ========== 仓颉 ⟷ C字符串转换 ==========
func stringLength(str: String): UInt64 {
// 将仓颉String转换为C字符串
let cStr = str.toCString()
let length = c_strlen(cStr)
// C字符串自动释放
return length
}
func copyString(source: String): String {
let srcCStr = source.toCString()
unsafe {
// 分配目标缓冲区
let destPtr = c_malloc((source.length() + 1) as UInt64)
let destCStr = CString.fromRawPointer(destPtr)
// 复制字符串
c_strcpy(destCStr, srcCStr)
// 转换回仓颉String
let result = destCStr.toString()
// 释放内存
c_free(destPtr)
return result
}
}
func compareStrings(s1: String, s2: String): Int32 {
let cStr1 = s1.toCString()
let cStr2 = s2.toCString()
return c_strcmp(cStr1, cStr2)
}
func testStrings() {
let text = "Hello, FFI!"
println("Length: ${stringLength(text)}") // 11
let copied = copyString(text)
println("Copied: ${copied}")
let cmp = compareStrings("abc", "def")
println("Compare result: ${cmp}") // 负数
}
字符串转换流程:

2.4 结构体类型映射
import std.ffi.*
// ========== C结构体定义 ==========
/*
// C代码
typedef struct {
int x;
int y;
} Point;
typedef struct {
char name[64];
int age;
double salary;
} Person;
*/
// ========== 仓颉结构体映射 ==========
@repr(C) // 使用C内存布局
struct Point {
x: Int32
y: Int32
}
@repr(C)
struct Person {
name: Array<UInt8, 64> // 固定大小数组
age: Int32
salary: Float64
}
// ========== C函数声明 ==========
@extern("C")
extern func distance(p1: &Point, p2: &Point): Float64
@extern("C")
extern func print_person(person: &Person)
// ========== 使用示例 ==========
func testStructs() {
let p1 = Point(x: 0, y: 0)
let p2 = Point(x: 3, y: 4)
let dist = distance(&p1, &p2)
println("Distance: ${dist}") // 5.0
// 创建Person结构
var person = Person(
name: Array::zero(), // 初始化为0
age: 30,
salary: 50000.0
)
// 填充name字段
let nameStr = "Alice"
for i in 0..<nameStr.length() {
person.name[i] = nameStr[i] as UInt8
}
print_person(&person)
}
结构体内存布局 :

2.5 数组和指针转换
import std.ffi.*
// ========== C数组函数 ==========
// C代码: int sum_array(int* arr, size_t len);
@extern("C")
extern func c_sum_array(arr: RawPointer, len: UInt64): Int32
// C代码: void fill_array(int* arr, size_t len, int value);
@extern("C")
@unsafe
extern func c_fill_array(arr: RawPointer, len: UInt64, value: Int32)
// ========== 仓颉包装函数 ==========
func sumArray(arr: &Array<Int32>): Int32 {
// 获取数组的原始指针
let ptr = arr.asRawPointer()
let len = arr.length() as UInt64
return c_sum_array(ptr, len)
}
func fillArray(arr: &mut Array<Int32>, value: Int32) {
unsafe {
let ptr = arr.asRawPointer()
let len = arr.length() as UInt64
c_fill_array(ptr, len, value)
}
}
func testArrays() {
let numbers = Array<Int32>([1, 2, 3, 4, 5])
let total = sumArray(&numbers)
println("Sum: ${total}") // 15
var buffer = Array<Int32>::withCapacity(10)
fillArray(&mut buffer, 42)
println("Buffer: ${buffer}") // [42, 42, ..., 42]
}
// ========== 动态分配数组 ==========
func allocateIntArray(size: UInt64): Result<ManagedArray<Int32>, Error> {
if size == 0 {
return Err(Error("Invalid size"))
}
unsafe {
let byteSize = size * sizeof<Int32>()
let ptr = c_malloc(byteSize)
if ptr.isNull() {
return Err(Error("Allocation failed"))
}
// 包装为托管数组
let array = ManagedArray<Int32>::fromRawPointer(ptr, size)
return Ok(array)
}
}
// ========== 托管数组包装 ==========
class ManagedArray<T> {
private ptr: RawPointer
private length: UInt64
private init(ptr: RawPointer, length: UInt64) {
this.ptr = ptr
this.length = length
}
public static func fromRawPointer(ptr: RawPointer, length: UInt64): ManagedArray<T> {
return ManagedArray<T>(ptr, length)
}
public func get(index: UInt64): T {
if index >= this.length {
throw IndexOutOfBoundsError()
}
unsafe {
let elementPtr = this.ptr.offset(index * sizeof<T>())
return elementPtr.read<T>()
}
}
public func set(index: UInt64, value: T) {
if index >= this.length {
throw IndexOutOfBoundsError()
}
unsafe {
let elementPtr = this.ptr.offset(index * sizeof<T>())
elementPtr.write<T>(value)
}
}
// 析构函数:自动释放内存
public func finalize() {
unsafe {
c_free(this.ptr)
}
}
}
三、C库集成实战
3.1 集成SQLite数据库
import std.ffi.*
// ========== SQLite类型定义 ==========
type SqlitePtr = RawPointer
type SqliteStmtPtr = RawPointer
// ========== SQLite函数绑定 ==========
@extern("C", library: "sqlite3")
extern func sqlite3_open(
filename: CString,
ppDb: &SqlitePtr
): Int32
@extern("C", library: "sqlite3")
extern func sqlite3_close(db: SqlitePtr): Int32
@extern("C", library: "sqlite3")
extern func sqlite3_prepare_v2(
db: SqlitePtr,
sql: CString,
nByte: Int32,
ppStmt: &SqliteStmtPtr,
pzTail: &CString
): Int32
@extern("C", library: "sqlite3")
extern func sqlite3_step(stmt: SqliteStmtPtr): Int32
@extern("C", library: "sqlite3")
extern func sqlite3_finalize(stmt: SqliteStmtPtr): Int32
@extern("C", library: "sqlite3")
extern func sqlite3_column_int(stmt: SqliteStmtPtr, iCol: Int32): Int32
@extern("C", library: "sqlite3")
extern func sqlite3_column_text(stmt: SqliteStmtPtr, iCol: Int32): CString
@extern("C", library: "sqlite3")
extern func sqlite3_errmsg(db: SqlitePtr): CString
// ========== 常量定义 ==========
let SQLITE_OK = 0
let SQLITE_ROW = 100
let SQLITE_DONE = 101
// ========== SQLite包装类 ==========
class SqliteDatabase {
private db: SqlitePtr
private init(db: SqlitePtr) {
this.db = db
}
public static func open(path: String): Result<SqliteDatabase, Error> {
var dbPtr: SqlitePtr = RawPointer.null()
let cPath = path.toCString()
let result = sqlite3_open(cPath, &dbPtr)
if result != SQLITE_OK {
return Err(Error("Failed to open database: ${path}"))
}
return Ok(SqliteDatabase(dbPtr))
}
public func execute(sql: String): Result<Unit, Error> {
var stmtPtr: SqliteStmtPtr = RawPointer.null()
var tail: CString = CString.null()
let cSql = sql.toCString()
let prepareResult = sqlite3_prepare_v2(
this.db,
cSql,
-1,
&stmtPtr,
&tail
)
if prepareResult != SQLITE_OK {
let errMsg = sqlite3_errmsg(this.db).toString()
return Err(Error("Prepare failed: ${errMsg}"))
}
let stepResult = sqlite3_step(stmtPtr)
sqlite3_finalize(stmtPtr)
if stepResult != SQLITE_DONE && stepResult != SQLITE_ROW {
let errMsg = sqlite3_errmsg(this.db).toString()
return Err(Error("Execute failed: ${errMsg}"))
}
return Ok(())
}
public func query(sql: String): Result<ArrayList<Row>, Error> {
var stmtPtr: SqliteStmtPtr = RawPointer.null()
var tail: CString = CString.null()
let cSql = sql.toCString()
let prepareResult = sqlite3_prepare_v2(
this.db,
cSql,
-1,
&stmtPtr,
&tail
)
if prepareResult != SQLITE_OK {
let errMsg = sqlite3_errmsg(this.db).toString()
return Err(Error("Prepare failed: ${errMsg}"))
}
let rows = ArrayList<Row>()
while true {
let stepResult = sqlite3_step(stmtPtr)
if stepResult == SQLITE_ROW {
let row = Row()
// 简化:假设两列 (id, name)
row.id = sqlite3_column_int(stmtPtr, 0)
row.name = sqlite3_column_text(stmtPtr, 1).toString()
rows.add(row)
} else if stepResult == SQLITE_DONE {
break
} else {
sqlite3_finalize(stmtPtr)
let errMsg = sqlite3_errmsg(this.db).toString()
return Err(Error("Query failed: ${errMsg}"))
}
}
sqlite3_finalize(stmtPtr)
return Ok(rows)
}
public func close() {
if !this.db.isNull() {
sqlite3_close(this.db)
this.db = RawPointer.null()
}
}
public func finalize() {
this.close()
}
}
struct Row {
id: Int32
name: String
}
// ========== 使用示例 ==========
func testSqlite() {
match SqliteDatabase.open("test.db") {
case Ok(db) => {
// 创建表
_ = db.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
)
""")
// 插入数据
_ = db.execute("INSERT INTO users (name) VALUES ('Alice')")
_ = db.execute("INSERT INTO users (name) VALUES ('Bob')")
// 查询数据
match db.query("SELECT id, name FROM users") {
case Ok(rows) => {
for row in rows {
println("User ${row.id}: ${row.name}")
}
}
case Err(e) => println("Query error: ${e.message}")
}
db.close()
}
case Err(e) => println("Open error: ${e.message}")
}
}
3.2 集成JSON解析库(cJSON)
import std.ffi.*
// ========== cJSON类型定义 ==========
type CJsonPtr = RawPointer
// ========== cJSON函数绑定 ==========
@extern("C", library: "cjson")
extern func cJSON_Parse(value: CString): CJsonPtr
@extern("C", library: "cjson")
extern func cJSON_Delete(item: CJsonPtr)
@extern("C", library: "cjson")
extern func cJSON_Print(item: CJsonPtr): CString
@extern("C", library: "cjson")
extern func cJSON_GetObjectItem(object: CJsonPtr, string: CString): CJsonPtr
@extern("C", library: "cjson")
extern func cJSON_IsString(item: CJsonPtr): Bool
@extern("C", library: "cjson")
extern func cJSON_IsNumber(item: CJsonPtr): Bool
@extern("C", library: "cjson")
extern func cJSON_GetStringValue(item: CJsonPtr): CString
@extern("C", library: "cjson")
extern func cJSON_GetNumberValue(item: CJsonPtr): Float64
// ========== JSON包装类 ==========
class JsonValue {
private ptr: CJsonPtr
private owned: Bool
private init(ptr: CJsonPtr, owned: Bool) {
this.ptr = ptr
this.owned = owned
}
public static func parse(json: String): Result<JsonValue, Error> {
let cStr = json.toCString()
let ptr = cJSON_Parse(cStr)
if ptr.isNull() {
return Err(Error("Invalid JSON"))
}
return Ok(JsonValue(ptr, owned: true))
}
public func getString(key: String): Option<String> {
let cKey = key.toCString()
let item = cJSON_GetObjectItem(this.ptr, cKey)
if item.isNull() || !cJSON_IsString(item) {
return None
}
let value = cJSON_GetStringValue(item).toString()
return Some(value)
}
public func getNumber(key: String): Option<Float64> {
let cKey = key.toCString()
let item = cJSON_GetObjectItem(this.ptr, cKey)
if item.isNull() || !cJSON_IsNumber(item) {
return None
}
return Some(cJSON_GetNumberValue(item))
}
public func toString(): String {
let cStr = cJSON_Print(this.ptr)
let result = cStr.toString()
unsafe { c_free(cStr.asRawPointer()) }
return result
}
public func finalize() {
if this.owned && !this.ptr.isNull() {
cJSON_Delete(this.ptr)
this.ptr = RawPointer.null()
}
}
}
// ========== 使用示例 ==========
func testJson() {
let jsonStr = """
{
"name": "Alice",
"age": 30,
"city": "Beijing"
}
"""
match JsonValue.parse(jsonStr) {
case Ok(json) => {
match json.getString("name") {
case Some(name) => println("Name: ${name}")
case None => println("Name not found")
}
match json.getNumber("age") {
case Some(age) => println("Age: ${age}")
case None => println("Age not found")
}
println("JSON: ${json.toString()}")
}
case Err(e) => println("Parse error: ${e.message}")
}
}
四、性能基准测试
4.1 基准测试框架
import std.time.*
import std.ffi.*
// ========== 基准测试工具 ==========
class Benchmark {
name: String
iterations: Int32
public init(name: String, iterations: Int32) {
this.name = name
this.iterations = iterations
}
public func run(block: () -> Unit) {
// 预热
for _ in 0..<10 {
block()
}
// 正式测试
let start = DateTime.now()
for _ in 0..<this.iterations {
block()
}
let end = DateTime.now()
let duration = end - start
let avgTime = duration.totalMilliseconds() / this.iterations as Float64
println("""
Benchmark: ${this.name}
Iterations: ${this.iterations}
Total time: ${duration.totalMilliseconds()}ms
Average: ${avgTime}ms
""")
}
}
// ========== 内存分配性能测试 ==========
func benchmarkMemoryAllocation() {
println("=== Memory Allocation Benchmark ===\n")
// 仓颉原生分配
Benchmark("Cangjie native allocation", 100000).run {
let arr = Array<Int32>::withCapacity(1000)
// 自动释放
}
// C malloc/free
Benchmark("C malloc/free", 100000).run {
unsafe {
let ptr = c_malloc(1000 * sizeof<Int32>())
c_free(ptr)
}
}
}
// ========== 数组操作性能测试 ==========
func benchmarkArrayOperations() {
println("=== Array Operations Benchmark ===\n")
let size = 10000
let arr = Array<Int32>::withCapacity(size)
for i in 0..<size {
arr.add(i)
}
// 仓颉数组求和
Benchmark("Cangjie array sum", 10000).run {
var sum = 0
for i in 0..<arr.length() {
sum += arr[i]
}
}
// C数组求和
Benchmark("C array sum", 10000).run {
let sum = sumArray(&arr)
}
}
// ========== 字符串处理性能测试 ==========
func benchmarkStringOperations() {
println("=== String Operations Benchmark ===\n")
let text = "Hello, World! This is a test string."
// 仓颉字符串长度
Benchmark("Cangjie string length", 1000000).run {
let len = text.length()
}
// C strlen
Benchmark("C strlen", 1000000).run {
let len = stringLength(text)
}
// 字符串拼接
Benchmark("String concatenation", 10000).run {
var result = ""
for i in 0..<100 {
result += "item${i}"
}
}
}
性能测试结果对比:

4.2 FFI调用开销分析
// ========== FFI调用开销测试 ==========
@extern("C")
extern func c_noop()
@extern("C")
extern func c_add_simple(a: Int32, b: Int32): Int32
func cangjie_add_simple(a: Int32, b: Int32): Int32 {
return a + b
}
func benchmarkFfiOverhead() {
println("=== FFI Call Overhead ===\n")
// 纯仓颉函数调用
Benchmark("Cangjie function call", 10000000).run {
let _ = cangjie_add_simple(10, 20)
}
// FFI函数调用
Benchmark("FFI function call", 10000000).run {
let _ = c_add_simple(10, 20)
}
// 空函数调用(测量调用开销)
Benchmark("Empty FFI call", 10000000).run {
c_noop()
}
}
FFI调用开销分解:

五、内存管理最佳实践
5.1 RAII模式实现
import std.ffi.*
// ========== RAII资源包装器 ==========
class CFile {
private handle: RawPointer
private init(handle: RawPointer) {
this.handle = handle
}
public static func open(path: String, mode: String): Result<CFile, Error> {
let cPath = path.toCString()
let cMode = mode.toCString()
let handle = unsafe {
c_fopen(cPath, cMode)
}
if handle.isNull() {
return Err(Error("Failed to open file: ${path}"))
}
return Ok(CFile(handle))
}
public func write(data: String): Result<Unit, Error> {
let cData = data.toCString()
let len = data.length() as UInt64
let written = unsafe {
c_fwrite(cData.asRawPointer(), 1, len, this.handle)
}
if written != len {
return Err(Error("Write failed"))
}
return Ok(())
}
public func read(size: UInt64): Result<String, Error> {
let buffer = unsafe {
c_malloc(size + 1)
}
if buffer.isNull() {
return Err(Error("Allocation failed"))
}
let readBytes = unsafe {
c_fread(buffer, 1, size, this.handle)
}
unsafe {
// 添加null终止符
let nullPtr = buffer.offset(readBytes)
nullPtr.write<UInt8>(0)
}
let result = CString.fromRawPointer(buffer).toString()
unsafe {
c_free(buffer)
}
return Ok(result)
}
// 析构函数:自动关闭文件
public func finalize() {
if !this.handle.isNull() {
unsafe {
c_fclose(this.handle)
}
this.handle = RawPointer.null()
}
}
}
// ========== C文件函数绑定 ==========
@extern("C")
@unsafe
extern func c_fopen(filename: CString, mode: CString): RawPointer
@extern("C")
@unsafe
extern func c_fclose(stream: RawPointer): Int32
@extern("C")
@unsafe
extern func c_fwrite(
ptr: RawPointer,
size: UInt64,
count: UInt64,
stream: RawPointer
): UInt64
@extern("C")
@unsafe
extern func c_fread(
ptr: RawPointer,
size: UInt64,
count: UInt64,
stream: RawPointer
): UInt64
// ========== 使用示例 ==========
func testCFile() {
// 文件自动关闭,无需手动管理
match CFile.open("test.txt", "w") {
case Ok(file) => {
_ = file.write("Hello, FFI!")
// file 离开作用域时自动关闭
}
case Err(e) => println("Error: ${e.message}")
}
// 读取文件
match CFile.open("test.txt", "r") {
case Ok(file) => {
match file.read(1024) {
case Ok(content) => println("Content: ${content}")
case Err(e) => println("Read error: ${e.message}")
}
}
case Err(e) => println("Open error: ${e.message}")
}
}
5.2 智能指针实现
import std.ffi.*
import std.concurrent.Atomic
// ========== 引用计数智能指针 ==========
class SharedPtr<T> {
private ptr: RawPointer
private refCount: &AtomicInt32
private deleter: (RawPointer) -> Unit
private init(
ptr: RawPointer,
refCount: &AtomicInt32,
deleter: (RawPointer) -> Unit
) {
this.ptr = ptr
this.refCount = refCount
this.deleter = deleter
}
public static func make(value: T): SharedPtr<T> {
let ptr = unsafe {
c_malloc(sizeof<T>())
}
unsafe {
ptr.write<T>(value)
}
let refCount = AtomicInt32(1)
let deleter = |p: RawPointer| {
unsafe { c_free(p) }
}
return SharedPtr<T>(ptr, &refCount, deleter)
}
public static func fromCPointer(
ptr: RawPointer,
deleter: (RawPointer) -> Unit
): SharedPtr<T> {
let refCount = AtomicInt32(1)
return SharedPtr<T>(ptr, &refCount, deleter)
}
// 拷贝构造
public func clone(): SharedPtr<T> {
this.refCount.fetchAdd(1)
return SharedPtr<T>(this.ptr, this.refCount, this.deleter)
}
public func get(): &T {
unsafe {
return this.ptr.readRef<T>()
}
}
public func getMut(): &mut T {
unsafe {
return this.ptr.readMutRef<T>()
}
}
public func refCount(): Int32 {
return this.refCount.load()
}
// 析构函数
public func finalize() {
let oldCount = this.refCount.fetchSub(1)
if oldCount == 1 {
// 最后一个引用,释放内存
this.deleter(this.ptr)
}
}
}
// ========== 使用示例 ==========
struct LargeObject {
data: Array<Int32, 1000>
id: Int32
}
func testSharedPtr() {
let obj = LargeObject(
data: Array::zero(),
id: 42
)
let ptr1 = SharedPtr<LargeObject>.make(obj)
println("Ref count: ${ptr1.refCount()}") // 1
let ptr2 = ptr1.clone()
println("Ref count: ${ptr1.refCount()}") // 2
{
let ptr3 = ptr1.clone()
println("Ref count: ${ptr1.refCount()}") // 3
} // ptr3 销毁
println("Ref count: ${ptr1.refCount()}") // 2
// ptr1 和 ptr2 离开作用域时,对象自动释放
}
5.3 内存泄漏检测
import std.ffi.*
import std.concurrent.Mutex
import std.collections.HashMap
// ========== 内存分配跟踪器 ==========
class MemoryTracker {
private allocations: HashMap<RawPointer, AllocationInfo>
private mutex: Mutex
private totalAllocated: Int64
private totalFreed: Int64
public init() {
this.allocations = HashMap<RawPointer, AllocationInfo>()
this.mutex = Mutex()
this.totalAllocated = 0
this.totalFreed = 0
}
public func trackAlloc(ptr: RawPointer, size: UInt64, location: String) {
let guard = this.mutex.lock()
let info = AllocationInfo(
size: size,
location: location,
timestamp: DateTime.now()
)
this.allocations.put(ptr, info)
this.totalAllocated += size as Int64
}
public func trackFree(ptr: RawPointer) {
let guard = this.mutex.lock()
match this.allocations.get(ptr) {
case Some(info) => {
this.totalFreed += info.size as Int64
this.allocations.remove(ptr)
}
case None => {
println("WARNING: Attempt to free untracked pointer")
}
}
}
public func printLeaks() {
let guard = this.mutex.lock()
println("=== Memory Leak Report ===")
println("Total allocated: ${this.totalAllocated} bytes")
println("Total freed: ${this.totalFreed} bytes")
println("Leaked: ${this.totalAllocated - this.totalFreed} bytes")
println("Leak count: ${this.allocations.size()}\n")
if this.allocations.size() > 0 {
println("Leaked allocations:")
for (ptr, info) in this.allocations {
println(" ${info.size} bytes at ${info.location}")
}
} else {
println("No leaks detected!")
}
}
}
struct AllocationInfo {
size: UInt64
location: String
timestamp: DateTime
}
// ========== 全局跟踪器 ==========
let memoryTracker = MemoryTracker()
// ========== 包装的分配函数 ==========
func trackedMalloc(size: UInt64, location: String): RawPointer {
let ptr = unsafe {
c_malloc(size)
}
if !ptr.isNull() {
memoryTracker.trackAlloc(ptr, size, location)
}
return ptr
}
func trackedFree(ptr: RawPointer) {
if !ptr.isNull() {
memoryTracker.trackFree(ptr)
unsafe {
c_free(ptr)
}
}
}
// ========== 使用示例 ==========
func testMemoryTracking() {
// 正常分配和释放
let ptr1 = trackedMalloc(1024, "testMemoryTracking:line1")
trackedFree(ptr1)
// 内存泄漏
let ptr2 = trackedMalloc(2048, "testMemoryTracking:line2")
// 忘记释放 ptr2
// 打印泄漏报告
memoryTracker.printLeaks()
}
六、高级FFI技巧
6.1 回调函数处理
import std.ffi.*
// ========== C回调函数类型 ==========
type CCallback = extern "C" func(data: RawPointer) -> Int32
// ========== C库函数:注册回调 ==========
@extern("C")
extern func register_callback(callback: CCallback, userData: RawPointer)
@extern("C")
extern func trigger_callback()
// ========== 仓颉回调包装 ==========
class CallbackWrapper {
private handler: (String) -> Int32
public init(handler: (String) -> Int32) {
this.handler = handler
}
// C兼容的回调函数
@extern("C")
public static func callbackTrampoline(data: RawPointer): Int32 {
unsafe {
// 从用户数据恢复仓颉闭包
let wrapper = data.read<&CallbackWrapper>()
let message = "Callback triggered"
return wrapper.handler(message)
}
}
public func register() {
let userData = unsafe {
let ptr = c_malloc(sizeof<&CallbackWrapper>())
ptr.write<&CallbackWrapper>(&this)
ptr
}
register_callback(CallbackWrapper.callbackTrampoline, userData)
}
}
// ========== 使用示例 ==========
func testCallback() {
let wrapper = CallbackWrapper { message =>
println("Received: ${message}")
return 0
}
wrapper.register()
trigger_callback()
}
6.2 可变参数函数
import std.ffi.*
// ========== C可变参数函数 ==========
// C代码: int printf(const char* format, ...);
@extern("C")
extern func c_printf(format: CString, ...): Int32
// ========== 仓颉包装(类型安全)==========
func printf(format: String, args: Array<FormatArg>) {
let cFormat = format.toCString()
// 根据参数类型调用
match args.length() {
0 => c_printf(cFormat)
1 => match args[0] {
case IntArg(value) => c_printf(cFormat, value)
case StringArg(value) => c_printf(cFormat, value.toCString())
case DoubleArg(value) => c_printf(cFormat, value)
}
// 更多参数情况...
}
}
enum FormatArg {
| IntArg(Int32)
| StringArg(String)
| DoubleArg(Float64)
}
func testPrintf() {
printf("Hello, %s!\n", [StringArg("World")])
printf("Number: %d\n", [IntArg(42)])
printf("Float: %.2f\n", [DoubleArg(3.14159)])
}
6.3 函数指针表
import std.ffi.*
// ========== C函数指针表结构 ==========
/*
// C代码
typedef struct {
int (*add)(int, int);
int (*multiply)(int, int);
void (*print)(const char*);
} MathOps;
*/
@repr(C)
struct MathOps {
add: extern "C" func(Int32, Int32) -> Int32
multiply: extern "C" func(Int32, Int32) -> Int32
print: extern "C" func(CString) -> Unit
}
// ========== 实现C函数 ==========
@extern("C")
func cj_add(a: Int32, b: Int32): Int32 {
return a + b
}
@extern("C")
func cj_multiply(a: Int32, b: Int32): Int32 {
return a * b
}
@extern("C")
func cj_print(msg: CString) {
println(msg.toString())
}
// ========== 创建函数表 ==========
func createMathOps(): MathOps {
return MathOps(
add: cj_add,
multiply: cj_multiply,
print: cj_print
)
}
// ========== C库函数:使用函数表 ==========
@extern("C")
extern func use_math_ops(ops: &MathOps)
func testFunctionPointers() {
let ops = createMathOps()
use_math_ops(&ops)
}
七、实战案例:图像处理库集成
7.1 集成libpng
import std.ffi.*
// ========== libpng类型定义 ==========
type PngStructPtr = RawPointer
type PngInfoPtr = RawPointer
// ========== libpng函数绑定(简化版)==========
@extern("C", library: "png")
extern func png_create_read_struct(
user_png_ver: CString,
error_ptr: RawPointer,
error_fn: RawPointer,
warn_fn: RawPointer
): PngStructPtr
@extern("C", library: "png")
extern func png_create_info_struct(png_ptr: PngStructPtr): PngInfoPtr
@extern("C", library: "png")
extern func png_destroy_read_struct(
png_ptr_ptr: &PngStructPtr,
info_ptr_ptr: &PngInfoPtr,
end_info_ptr_ptr: &PngInfoPtr
)
@extern("C", library: "png")
extern func png_init_io(png_ptr: PngStructPtr, fp: RawPointer)
@extern("C", library: "png")
extern func png_read_info(png_ptr: PngStructPtr, info_ptr: PngInfoPtr)
@extern("C", library: "png")
extern func png_get_image_width(png_ptr: PngStructPtr, info_ptr: PngInfoPtr): UInt32
@extern("C", library: "png")
extern func png_get_image_height(png_ptr: PngStructPtr, info_ptr: PngInfoPtr): UInt32
@extern("C", library: "png")
extern func png_read_image(png_ptr: PngStructPtr, row_pointers: RawPointer)
// ========== PNG图像类 ==========
class PngImage {
width: UInt32
height: UInt32
data: Array<UInt8>
private init(width: UInt32, height: UInt32, data: Array<UInt8>) {
this.width = width
this.height = height
this.data = data
}
public static func load(path: String): Result<PngImage, Error> {
// 打开文件
let file = match CFile.open(path, "rb") {
case Ok(f) => f
case Err(e) => return Err(e)
}
// 创建PNG读取结构
let pngPtr = png_create_read_struct(
"1.6.37".toCString(),
RawPointer.null(),
RawPointer.null(),
RawPointer.null()
)
if pngPtr.isNull() {
return Err(Error("Failed to create PNG read struct"))
}
let infoPtr = png_create_info_struct(pngPtr)
if infoPtr.isNull() {
png_destroy_read_struct(&pngPtr, &RawPointer.null(), &RawPointer.null())
return Err(Error("Failed to create PNG info struct"))
}
// 设置文件IO
png_init_io(pngPtr, file.handle)
// 读取图像信息
png_read_info(pngPtr, infoPtr)
let width = png_get_image_width(pngPtr, infoPtr)
let height = png_get_image_height(pngPtr, infoPtr)
// 分配图像数据
let dataSize = (width * height * 4) as UInt64 // RGBA
let data = Array<UInt8>::withCapacity(dataSize as Int64)
// 读取图像数据
// ... 实现细节省略
// 清理
png_destroy_read_struct(&pngPtr, &infoPtr, &RawPointer.null())
return Ok(PngImage(width, height, data))
}
public func getPixel(x: UInt32, y: UInt32): Rgba {
let index = ((y * this.width + x) * 4) as Int64
return Rgba(
r: this.data[index],
g: this.data[index + 1],
b: this.data[index + 2],
a: this.data[index + 3]
)
}
public func setPixel(x: UInt32, y: UInt32, color: Rgba) {
let index = ((y * this.width + x) * 4) as Int64
this.data[index] = color.r
this.data[index + 1] = color.g
this.data[index + 2] = color.b
this.data[index + 3] = color.a
}
}
struct Rgba {
r: UInt8
g: UInt8
b: UInt8
a: UInt8
}
// ========== 使用示例 ==========
func testPngImage() {
match PngImage.load("image.png") {
case Ok(image) => {
println("Image size: ${image.width}x${image.height}")
// 获取像素
let pixel = image.getPixel(10, 10)
println("Pixel at (10,10): RGB(${pixel.r}, ${pixel.g}, ${pixel.b})")
// 修改像素
image.setPixel(10, 10, Rgba(r: 255, g: 0, b: 0, a: 255))
}
case Err(e) => println("Error: ${e.message}")
}
}
八、总结与最佳实践
8.1 FFI使用原则

8.2 性能优化清单
✅ 减少FFI调用次数
- 批量传递数据而非单个元素
- 缓存频繁访问的C函数结果
- 在C层实现计算密集型循环
✅ 优化内存传递
- 使用引用而非值传递大对象
- 避免不必要的内存复制
- 考虑使用共享内存
✅ 正确使用内存管理
- 优先使用RAII模式
- 实现智能指针封装C指针
- 及时释放不再使用的资源
✅ 类型转换优化
- 最小化字符串转换
- 使用零拷贝技术
- 预分配缓冲区
8.3 常见陷阱
❌ 陷阱1:内存泄漏
// 错误:忘记释放
func bad() {
let ptr = unsafe { c_malloc(1024) }
// 忘记调用 c_free(ptr)
}
// 正确:使用RAII
func good() {
let managed = ManagedPointer.alloc(1024)
// 自动释放
}
❌ 陷阱2:悬空指针
// 错误:返回局部变量的指针
func bad(): RawPointer {
let local = 42
return &local // ❌ 悬空指针!
}
// 正确:使用堆分配
func good(): RawPointer {
let ptr = unsafe { c_malloc(sizeof<Int32>()) }
unsafe { ptr.write<Int32>(42) }
return ptr
}
❌ 陷阱3:ABI不兼容
// 错误:忘记@repr(C)
struct Bad { // ❌ 内存布局不确定
a: Int32
b: Float64
}
// 正确:显式指定C布局
@repr(C)
struct Good { // ✅ C兼容
a: Int32
b: Float64
}
九、总结
核心知识点回顾
- FFI原理:理解仓颉与C/C++互操作的底层机制
- 类型映射:掌握各种数据类型的转换规则
- 内存管理:使用RAII和智能指针确保安全
- 性能优化:减少跨边界调用,优化数据传递
- 实战应用:集成SQLite、JSON、PNG等库
讨论问题
- 设计权衡:什么时候应该使用FFI?什么时候应该纯仓颉实现?
- 安全性:如何在性能和安全性之间找到平衡?
- 可维护性:如何设计易于维护的FFI绑定层?
学习建议
- 🔹 深入理解ABI:学习不同平台的调用约定
- 🔹 实践项目:集成实际的C/C++库
- 🔹 性能分析:使用工具诊断FFI性能问题
- 🔹 安全审计:定期检查内存安全和资源泄漏