> For the complete documentation index, see [llms.txt](https://nongchatea.gitbook.io/koffi-chinese/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://nongchatea.gitbook.io/koffi-chinese/shu-ru-can-shu.md).

# 输入参数

## 原始类型

### 标准类型

虽然 C 标准允许大多数整数类型的大小存在变化，但 Koffi 对大多数基本类型强制执行相同的定义，如下所示：

| C类型                           | JS类型             | Bytes | Signedness | 备注        |
| ----------------------------- | ---------------- | ----- | ---------- | --------- |
| void                          | Undefined        | 0     |            | 仅作为返回类型有效 |
| int8, int8\_t                 | Number (integer) | 1     | Signed     |           |
| uint8, uint8\_t               | Number (integer) | 1     | Unsigned   |           |
| char                          | Number (integer) | 1     | Signed     |           |
| uchar, unsigned char          | Number (integer) | 1     | Unsigned   |           |
| char16, char16\_t             | Number (integer) | 2     | Signed     |           |
| int16, int16\_t               | Number (integer) | 2     | Signed     |           |
| uint16, uint16\_t             | Number (integer) | 2     | Unsigned   |           |
| short                         | Number (integer) | 2     | Signed     |           |
| ushort, unsigned short        | Number (integer) | 2     | Unsigned   |           |
| int32, int32\_t               | Number (integer) | 4     | Signed     |           |
| uint32, uint32\_t             | Number (integer) | 4     | Unsigned   |           |
| int                           | Number (integer) | 4     | Signed     |           |
| uint, unsigned int            | Number (integer) | 4     | Unsigned   |           |
| int64, int64\_t               | Number (integer) | 8     | Signed     |           |
| uint64, uint64\_t             | Number (integer) | 8     | Unsigned   |           |
| longlong, long long           | Number (integer) | 8     | Signed     |           |
| ulonglong, unsigned long long | Number (integer) | 8     | Unsigned   |           |
| float32                       | Number (float)   | 4     |            |           |
| float64                       | Number (float)   | 8     |            |           |
| float                         | Number (float)   | 4     |            |           |
| double                        | Number (float)   | 8     |            |           |

从 JS 整数转换为 C 整数时，Koffi 还接受 BigInt 值。如果该值超出了 C 类型的范围，Koffi 会将数字转换为未定义的值。相反，当需要 64 位大整数时，会自动使用 BigInt 值。

Koffi 定义了更多类型，可以根据操作系统和架构改变大小：

| C类型             | JS类型             | Signedness | 备注                            |
| --------------- | ---------------- | ---------- | ----------------------------- |
| bool            | Boolean          |            | 通常为一个字节                       |
| long            | Number (integer) | Signed     | 4 或 8 个字节，具体取决于平台（LP64、LLP64） |
| ulong           | Number (integer) | Unsigned   | 4 或 8 个字节，具体取决于平台（LP64、LLP64） |
| unsigned long   | Number (integer) | Unsigned   | 4 或 8 个字节，具体取决于平台（LP64、LLP64） |
| intptr          | Number (integer) | Signed     | 4 或 8 个字节，具体取决于寄存器宽度          |
| intptr\_t       | Number (integer) | Signed     | 4 或 8 个字节，具体取决于寄存器宽度          |
| uintptr         | Number (integer) | Unsigned   | 4 或 8 个字节，具体取决于寄存器宽度          |
| uintptr\_t      | Number (integer) | Unsigned   | 4 或 8 个字节，具体取决于寄存器宽度          |
| str, string     | String           |            | JS 字符串与 UTF-8 相互转换            |
| str16, string16 | String           |            | JS 字符串与 UTF-16 (LE) 相互转换      |

基元类型可以通过名称（在字符串中）或通过以下方式指定`koffi.types`<br>

```typescript
// These two lines do the same:
let struct1 = koffi.struct({ dummy: 'long' });
let struct2 = koffi.struct({ dummy: koffi.types.long });
```

### 字节序敏感的整数(大小端存储)

*Koffi 2.1 中的新功能*

Koffi 定义了一堆字节序敏感类型，可以在处理二进制数据（网络负载、二进制文件格式等）时使用。

| C                         | 字节长度 | Signedness | 大小端存储         |
| ------------------------- | ---- | ---------- | ------------- |
| int16\_le, int16\_le\_t   | 2    | Signed     | Little Endian |
| int16\_be, int16\_be\_t   | 2    | Signed     | Big Endian    |
| uint16\_le, uint16\_le\_t | 2    | Unsigned   | Little Endian |
| uint16\_be, uint16\_be\_t | 2    | Unsigned   | Big Endian    |
| int32\_le, int32\_le\_t   | 4    | Signed     | Little Endian |
| int32\_be, int32\_be\_t   | 4    | Signed     | Big Endian    |
| uint32\_le, uint32\_le\_t | 4    | Unsigned   | Little Endian |
| uint32\_be, uint32\_be\_t | 4    | Unsigned   | Big Endian    |
| int64\_le, int64\_le\_t   | 8    | Signed     | Little Endian |
| int64\_be, int64\_be\_t   | 8    | Signed     | Big Endian    |
| uint64\_le, uint64\_le\_t | 8    | Unsigned   | Little Endian |
| uint64\_be, uint64\_be\_t | 8    | Unsigned   | Big Endian    |

## 结构类型

### 结构体定义

Koffi 将 JS 对象转换为 C 结构，反之亦然。

与函数声明不同，到目前为止，只有一种方法可以使用函数创建结构类型`koffi.struct()`。该函数有两个参数：第一个是类型的名称，第二个是包含结构成员名称和类型的对象。您可以省略第一个参数来声明匿名结构。

以下示例说明了如何使用 Koffi 在 C 和 JS 中声明相同的结构：

```c
typedef struct A {
    int a;
    char b;
    const char *c;
    struct {
        double d1;
        double d2;
    } d;
} A;

```

```typescript
const A = koffi.struct('A', {
    a: 'int',
    b: 'char',
    c: 'const char *', // Koffi does not care about const, it is ignored
    d: koffi.struct({
        d1: 'double',
        d2: 'double'
    })
});
```

Koffi 在对齐和填充方面自动遵循平台 C ABI。但是，如果需要，您可以通过以下方式覆盖这些规则：

* `koffi.pack()`打包所有成员，不使用（而不是`koffi.struct()`）填充
* 更改特定成员的对齐方式，如下所示

```typescript
// This struct is 3 bytes long
const PackedStruct = koffi.pack('PackedStruct', {
    a: 'int8_t',
    b: 'int16_t'
});

// This one is 10 bytes long, the second member has an alignment requirement of 8 bytes
const BigStruct = koffi.struct('BigStruct', {
    a: 'int8_t',
    b: [8, 'int16_t']
})
```

声明结构后，您可以通过名称（使用字符串，就像对基本类型所做的那样）或通过调用返回的值来使用它`koffi.struct()`。声明匿名结构时，只有后者是可能的。

```typescript
// The following two function declarations are equivalent, and declare a function taking an A value and returning A
const Function1 = lib.func('A Function(A value)');
const Function2 = lib.func('Function', A, [A]);
```

### 不透明类型

许多 C 库使用某种面向对象的 API，带有一对专用于创建和删除对象的函数。一个明显的例子可以在 stdio.h 中找到，带有不透明`FILE *`指针。您可以使用`fopen()`和`fclose()`打开和关闭文件，并使用其他函数（例如`fread()`或 `ftell()`）操作不透明指针。

在 Koffi 中，您可以使用不透明类型来管理它。使用 声明不透明类型`koffi.opaque(name)`，并使用指向该类型的指针作为返回类型或某种[输出参数](https://koffi.dev/output)（使用双指针）。

{% hint style="info" %}
不透明类型**在 2.0 版中发生了变化，在 2.1 版中又发生了变化**。

在 Koffi 1.x 中，不透明句柄的定义方式使它们可以直接用作参数和返回类型，从而掩盖了底层指针。

现在，您必须通过指针使用它们，并使用数组作为输出参数。这如下面的示例所示（在 JS 部分中查找对 的调用），并在[输出参数](https://koffi.dev/output)`ConcatNewOut`部分中进行了描述。

除此之外，您应该使用`koffi.opaque()`（在 Koffi 2.1 中引入）而不是`koffi.handle()`已弃用的，并且最终将在 Koffi 3.0 中删除。

有关更多信息，请参阅[迁移指南。](https://koffi.dev/migration)
{% endhint %}

下面的完整示例在 C 中实现了迭代字符串生成器（连接器），并从 Javascript 使用它来输出 Hello World 和 FizzBu​​zz 的混合内容。构建器隐藏在不透明类型后面，并使用一对 C 函数创建和销毁：`ConcatNew`(or `ConcatNewOut`) 和`ConcatFree`。

```c
// Build with: clang -fPIC -o handles.so -shared handles.c -Wall -O2

#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

typedef struct Fragment {
    struct Fragment *next;

    size_t len;
    char str[];
} Fragment;

typedef struct Concat {
    Fragment *first;
    Fragment *last;

    size_t total;
} Concat;

bool ConcatNewOut(Concat **out)
{
    Concat *c = malloc(sizeof(*c));
    if (!c) {
        fprintf(stderr, "Failed to allocate memory: %s\n", strerror(errno));
        return false;
    }

    c->first = NULL;
    c->last = NULL;
    c->total = 0;

    *out = c;
    return true;
}

Concat *ConcatNew()
{
    Concat *c = NULL;
    ConcatNewOut(&c);
    return c;
}

void ConcatFree(Concat *c)
{
    if (!c)
        return;

    Fragment *f = c->first;

    while (f) {
        Fragment *next = f->next;
        free(f);
        f = next;
    }

    free(c);
}

bool ConcatAppend(Concat *c, const char *frag)
{
    size_t len = strlen(frag);

    Fragment *f = malloc(sizeof(*f) + len + 1);
    if (!f) {
        fprintf(stderr, "Failed to allocate memory: %s\n", strerror(errno));
        return false;
    }

    f->next = NULL;
    if (c->last) {
        c->last->next = f;
    } else {
        c->first = f;
    }
    c->last = f;
    c->total += len;

    f->len = len;
    memcpy(f->str, frag, len);
    f->str[len] = 0;

    return true;
}

const char *ConcatBuild(Concat *c)
{
    Fragment *r = malloc(sizeof(*r) + c->total + 1);
    if (!r) {
        fprintf(stderr, "Failed to allocate memory: %s\n", strerror(errno));
        return NULL;
    }

    r->next = NULL;
    r->len = 0;

    Fragment *f = c->first;

    while (f) {
        Fragment *next = f->next;

        memcpy(r->str + r->len, f->str, f->len);
        r->len += f->len;

        free(f);
        f = next;
    }
    r->str[r->len] = 0;

    c->first = r;
    c->last = r;

    return r->str;
}
```

```typescript
// ES6 syntax: import koffi from 'koffi';
const koffi = require('koffi');

const lib = koffi.load('./handles.so');

const Concat = koffi.opaque('Concat');
const ConcatNewOut = lib.func('bool ConcatNewOut(_Out_ Concat **out)');
const ConcatNew = lib.func('Concat *ConcatNew()');
const ConcatFree = lib.func('void ConcatFree(Concat *c)');
const ConcatAppend = lib.func('bool ConcatAppend(Concat *c, const char *frag)');
const ConcatBuild = lib.func('const char *ConcatBuild(Concat *c)');

let c = ConcatNew();
if (!c) {
    // This is stupid, it does the same, but try both versions (return value, output parameter)
    let ptr = [null];
    if (!ConcatNewOut(ptr))
        throw new Error('Allocation failure');
    c = ptr[0];
}

try {
    if (!ConcatAppend(c, 'Hello... '))
        throw new Error('Allocation failure');
    if (!ConcatAppend(c, 'World!\n'))
        throw new Error('Allocation failure');

    for (let i = 1; i <= 30; i++) {
        let frag;
        if (i % 15 == 0) {
            frag = 'FizzBuzz';
        } else if (i % 5 == 0) {
            frag = 'Buzz';
        } else if (i % 3 == 0) {
            frag = 'Fizz';
        } else {
            frag = String(i);
        }

        if (!ConcatAppend(c, frag))
            throw new Error('Allocation failure');
        if (!ConcatAppend(c, ' '))
            throw new Error('Allocation failure');
    }

    let str = ConcatBuild(c);
    if (str == null)
        throw new Error('Allocation failure');
    console.log(str);
} finally {
    ConcatFree(c);
}
```

## 数组类型

### 固定大小的 C 数组

固定大小的数组用`koffi.array(type, length)`声明。就像在 C 中一样，它们不能作为函数参数传递（它们退化为指针），也不能按值返回。但是，您可以将它们嵌入到结构类型中。

在将数组传入/传出 C 时，Koffi 应用以下转换规则：

* **JS 到 C**：Koffi 可以采用普通数组（例如`[1,2]`）或正确类型的 TypedArray（e.g.`Uint8Array` for an array of `uint8_t` numbers）
* **C 到 JS**（返回值、输出参数、回调）：如果可能，Koffi 将使用 TypedArray。但是，当您使用可选的提示参数创建数组类型时，您可以更改此行为：`koffi.array('uint8_t', 64, 'Array')`。对于非数字类型，例如字符串或结构数组，Koffi 创建普通数组。

请参阅下面的示例：

```typescript
// ES6 syntax: import koffi from 'koffi';
const koffi = require('koffi');

// Those two structs are exactly the same, only the array conversion hint is different
const Foo1 = koffi.struct('Foo', {
    i: 'int',
    a16: koffi.array('int16_t', 8)
});
const Foo2 = koffi.struct('Foo', {
    i: 'int',
    a16: koffi.array('int16_t', 8, 'Array')
});

// Uses an hypothetical C function that just returns the struct passed as a parameter
const ReturnFoo1 = lib.func('Foo1 ReturnFoo(Foo1 p)');
const ReturnFoo2 = lib.func('Foo2 ReturnFoo(Foo2 p)');

console.log(ReturnFoo1({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: Int16Array(2) [6, 8] }
console.log(ReturnFoo2({ i: 5, a16: [6, 8] })) // Prints { i: 5, a16: [6, 8] }
```

### 固定大小的字符串缓冲区

Koffi还可以在以下情况下将JS字符串转换为固定大小的数组：

* **char 数组**用 UTF-8 编码的字符串填充，如果需要的话会被截断。缓冲区始终以 NUL 结尾。
* **char16（或 char16\_t）数组**用 UTF-16 编码的字符串填充，如果需要的话会被截断。缓冲区始终以 NUL 结尾。

反之亦然，Koffi 可以将 C 固定大小缓冲区转换为 JS 字符串。`String`对于 char、char16 和 char16\_t 数组，默​​认情况下会发生这种情况，但您也可以使用数组提示（例如`koffi.array('char', 8, 'String')`）显式请求此情况。

### 动态数组（指针）

在 C 中，动态大小的数组通常作为指针传递。在相关部分中阅读有关[数组指针的更多信息。](https://koffi.dev/pointers#array-pointers-dynamic-arrays)

## 联合类型

[联合类型](https://koffi.dev/unions)的声明和使用将在后面的章节中解释，这里仅在需要时简单提及。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://nongchatea.gitbook.io/koffi-chinese/shu-ru-can-shu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
