快速开始

每个容器 5 分钟上手,完整可编译示例。按 ccmap → cctreap 顺序排列。

1. 引入头文件

ccalg 是 header-only 库,无需编译链接。两种引入方式:

方式一 — 直接从仓库 include 目录:

#include "ccmap.h"
// gcc -I path/to/ccalg/include main.c

方式二 — 安装后(推荐):

cmake --install build --prefix /usr/local
#include "ccalg/ccmap.h"      // 红黑树
#include "ccalg/cchashmap.h"  // 哈希表
#include "ccalg/cclink.h"     // 单向链表
#include "ccalg/cclist.h"     // 双向链表
#include "ccalg/ccheap.h"     // 优先队列
#include "ccalg/ccunicode.h"  // UTF-8 编解码
#include "ccalg/ccbits.h"     // 位运算原语

2. ccmap — 红黑树(有序映射)

#include <stdio.h>
#include "ccmap.h"

struct entry { int key; ccmap_node_t node; };

/* 比较函数:按 key 升序 */
static int64_t cmp(const ccmap_node_t *a, const ccmap_node_t *b) {
    return (int64_t)CCMAP_CONTAINER(a, struct entry, node)->key
         - (int64_t)CCMAP_CONTAINER(b, struct entry, node)->key;
}

int main() {
    ccmap_t m; ccmap_init(&m, cmp);

    struct entry e1 = {42}, e2 = {7}, e3 = {99};
    ccmap_insert(&m, &e1.node, NULL);
    ccmap_insert(&m, &e2.node, NULL);
    ccmap_insert(&m, &e3.node, NULL);

    /* 有序遍历: 7 → 42 → 99 */
    for (ccmap_node_t *n = ccmap_begin(&m); n != ccmap_end(&m); n = ccmap_next(n))
        printf("%d\n", CCMAP_CONTAINER(n, struct entry, node)->key);

    /* 查找 */
    struct entry probe = {42};
    ccmap_node_t *f = ccmap_find(&m, &probe.node);
    if (f) printf("found: %d\n", CCMAP_CONTAINER(f, struct entry, node)->key);

    return 0;
}

零开销模式 — 用宏替代函数指针,消除间接调用:

#define CCMAP_COMPARE(a, b) \
    ((int64_t)CCMAP_CONTAINER(a, struct entry, node)->key \
   - (int64_t)CCMAP_CONTAINER(b, struct entry, node)->key)
#include "ccmap.h"
/* ccmap_init(&m, NULL) — 第二个参数被忽略 */

3. cchashmap — 哈希表

#include <stdio.h>
#include "cchashmap.h"

struct entry { int key; cchashmap_node_t node; };

static uint64_t hash(const cchashmap_node_t *n, size_t seed) {
    (void)seed;
    return CCHASHMAP_CONTAINER(n, struct entry, node)->key;
}
static bool equal(const cchashmap_node_t *a, const cchashmap_node_t *b) {
    return CCHASHMAP_CONTAINER(a, struct entry, node)->key
        == CCHASHMAP_CONTAINER(b, struct entry, node)->key;
}

int main() {
    cchashmap_t m; cchashmap_init(&m, hash, equal);

    struct entry e = {42};
    cchashmap_node_t *old;
    if (cchashmap_set(&m, &e.node, &old))
        printf("inserted\n");
    else
        printf("duplicate, existing node: %p\n", (void*)old);

    /* 查找 */
    struct entry probe = {42};
    cchashmap_node_t *f = cchashmap_get(&m, &probe.node);

    /* 删除 + 销毁 */
    cchashmap_del(&m, &e.node);
    cchashmap_destroy(&m);
    return 0;
}
#include <stdio.h>
#include "cclink.h"

struct entry { int val; cclink_node_t node; };

int main() {
    cclink_t l; cclink_init(&l);

    struct entry e1 = {1}, e2 = {2}, e3 = {3};
    cclink_push_back(&l, &e1.node);
    cclink_push_back(&l, &e2.node);
    cclink_push_back(&l, &e3.node);

    /* 正向遍历: 1, 2, 3 */
    for (cclink_node_t *n = cclink_begin(&l); n != cclink_end(&l); n = cclink_next(n))
        printf("%d\n", CCLINK_CONTAINER(n, struct entry, node)->val);

    /* pop_front: 取出头部 */
    while (!cclink_empty(&l)) {
        cclink_node_t *n = cclink_pop_front(&l);
        printf("popped: %d\n", CCLINK_CONTAINER(n, struct entry, node)->val);
    }

    return 0;
}

5. cclist — 双向链表

#include <stdio.h>
#include "cclist.h"

struct entry { int val; cclist_node_t node; };

int main() {
    cclist_t l; cclist_init(&l);

    struct entry e1 = {1}, e2 = {2}, e3 = {3};
    cclist_push_back(&l, &e1.node);
    cclist_push_back(&l, &e2.node);
    cclist_push_back(&l, &e3.node);

    /* 正向遍历: 1, 2, 3 */
    for (cclist_node_t *n = cclist_begin(&l); n != cclist_end(&l); n = cclist_next(n))
        printf("%d\n", CCLIST_CONTAINER(n, struct entry, node)->val);

    /* splice: 合并两个链表 */
    cclist_t src; cclist_init(&src);
    struct entry e4 = {4}, e5 = {5};
    cclist_push_back(&src, &e4.node);
    cclist_push_back(&src, &e5.node);
    cclist_splice_back(&l, &src);  // src 变空, l = [1,2,3,4,5]

    /* 验证双向不变量 */
    if (cclist_verify(&l) == CCLIST_NOERROR)
        printf("list is valid\n");

    return 0;
}

6. ccheap — 优先队列 / 定时器

ccheap_node_t 是一个 8 字节 union,包含 prioritytimeout——堆不访问字段,由比较器定义语义。

#include <stdio.h>
#define CCHEAP_COMPARE(a, b) \
    ((int64_t)(b)->priority - (int64_t)(a)->priority)
#include "ccheap.h"

int main() {
    ccheap_t h;
    ccheap_init(&h, NULL);  // CCHEAP_COMPARE 定义后第二个参数被忽略

    ccheap_node_t n1 = {.priority = 50};
    ccheap_node_t n2 = {.priority = 10};
    ccheap_node_t n3 = {.priority = 30};
    ccheap_push(&h, &n1);
    ccheap_push(&h, &n2);
    ccheap_push(&h, &n3);

    /* 弹出顺序: .priority = 10, 30, 50 */
    while (ccheap_size(&h) > 0) {
        ccheap_node_t *top = ccheap_pop(&h);
        printf("%llu\n", (unsigned long long)top->priority);
    }

    ccheap_destroy(&h);
    return 0;
}

嵌入自定义结构体(携带额外数据)

struct task { ccheap_node_t node; void (*cb)(void *); void *arg; };

static int64_t cmp(const ccheap_node_t *a, const ccheap_node_t *b) {
    return (int64_t)b->timeout - (int64_t)a->timeout;
}

/* ccheap_t h; ccheap_init(&h, cmp); */
/* struct task t = {{.timeout = now() + 5000}, my_cb, NULL}; */
/* ccheap_push(&h, &t.node); */
/* struct task *done = CCHEAP_CONTAINER(ccheap_pop(&h), struct task, node); */
/* done->cb(done->arg); */

零开销宏模式

#define CCHEAP_COMPARE(a, b) \
    ((int64_t)(b)->timeout - (int64_t)(a)->timeout)
#include "ccheap.h"
/* ccheap_init(&h, NULL) — 第二个参数被忽略 */

7. ccflatmap — 排序数组映射

#include <stdio.h>
#include "ccflatmap.h"

int main() {
    ccflatmap_t m; ccflatmap_init(&m, NULL);

    /* 批量插入:push_back + sort(推荐) */
    ccflatmap_reserve(&m, 3);
    ccflatmap_node_t n1 = {.key = 42, .value = 100};
    ccflatmap_node_t n2 = {.key = 7,  .value = 200};
    ccflatmap_node_t n3 = {.key = 99, .value = 300};
    ccflatmap_push_back(&m, n1);
    ccflatmap_push_back(&m, n2);
    ccflatmap_push_back(&m, n3);
    ccflatmap_sort(&m);

    /* 二分查找 */
    ccflatmap_node_t probe = {.key = 7};
    ccflatmap_node_t *f = ccflatmap_find(&m, &probe);
    if (f) printf("found: %lld\n", (long long)f->value);

    ccflatmap_destroy(&m);
    return 0;
}

8. cctreap — Treap(排行树)

#include <stdio.h>
#include <stdlib.h>
#include "cctreap.h"

struct entry { int key; cctreap_node_t node; };

static int64_t key_cmp(const cctreap_node_t *a, const cctreap_node_t *b) {
    return CCTREAP_CONTAINER(a, struct entry, node)->key
         - CCTREAP_CONTAINER(b, struct entry, node)->key;
}

int main() {
    cctreap_t t; cctreap_init(&t, key_cmp);

    struct entry e[5];
    for (int i = 0; i < 5; i++) {
        e[i].key = i * 10;
        cctreap_insert(&t, &e[i].node, NULL);
    }

    /* 第 k 小(0-indexed) */
    cctreap_node_t *k2 = cctreap_kth(&t, 2);
    printf("kth(2) = %d\n", CCTREAP_CONTAINER(k2, struct entry, node)->key); // 20

    /* 排名 */
    int64_t r = cctreap_rank(&t, &e[4].node);
    printf("rank(40) = %lld\n", (long long)r); // 4

    return 0;
}

9. ccvector — 动态数组

#include <stdio.h>
#include "ccvector.h"

int main() {
    ccvector_t v; ccvector_init(&v);

    for (int i = 0; i < 5; i++) {
        ccvector_node_t e = {.value = (uint32_t)i};
        ccvector_push_back(&v, e);
    }

    for (size_t i = 0; i < ccvector_size(&v); i++)
        printf("%u\n", ccvector_at(&v, i)->value);
    /* 输出: 0, 1, 2, 3, 4 */

    ccvector_pop_back(&v);  /* 移除 4 */
    ccvector_destroy(&v);
    return 0;
}

自定义元素类型

#define CCVECTOR_NODE_T struct point
struct point { float x, y; };
#include "ccvector.h"
/* 现在 ccvector 存储 struct point 值 */

10. ccunicode — UTF-8 编解码

#include <stdio.h>
#include <string.h>
#include "ccunicode.h"

int main() {
    /* ── 解码:UTF-8 → UCS-4 ── */
    const char *utf8 = "A©你";
    int offset = 0, len = (int)strlen(utf8);

    while (offset < len) {
        uint32_t cp;
        int n = ccunicode_to_codepoint(utf8 + offset, len - offset, &cp);
        if (n == 0) { printf("invalid UTF-8\n"); break; }
        printf("U+%04X  (%d bytes)\n", cp, n);
        offset += n;
    }
    /* 输出:
       U+0041  (1 byte)   — ASCII 'A'
       U+00A9  (2 bytes)  — ©
       U+4F60  (3 bytes)  — 你
    */

    /* ── 编码:UCS-4 → UTF-8 ── */
    uint32_t codepoints[] = { 0x41, 0xA9, 0x4F60, 0x1F600 };  /* A, ©, 你, 😀 */
    char buf[4]; int n;

    for (size_t i = 0; i < sizeof(codepoints)/sizeof(codepoints[0]); i++) {
        if (ccunicode_from_codepoint(codepoints[i], buf, &n)) {
            printf("U+%05X → ", codepoints[i]);
            for (int j = 0; j < n; j++) printf("%02X ", (unsigned char)buf[j]);
            printf(" (%d bytes)\n", n);
        } else {
            printf("U+%05X → invalid\n", codepoints[i]);
        }
    }
    /* 输出:
       U+00041 → 41        (1 byte)
       U+000A9 → C2 A9     (2 bytes)
       U+04F60 → E4 BD A0  (3 bytes)
       U+1F600 → F0 9F 98 80 (4 bytes)
    */

    /* ── 非法输入 ── */
    uint32_t cp;
    printf("%d\n", ccunicode_to_codepoint("\xC0\x80", 2, &cp));  // 0 — 超长编码
    printf("%d\n", ccunicode_to_codepoint("\xED\xA0\x80", 3, &cp)); // 0 — 代理对
    printf("%d\n", ccunicode_from_codepoint(0xD800, buf, &n));       // false — 代理对
    printf("%d\n", ccunicode_from_codepoint(0x110000, buf, &n));     // false — 超范围

    return 0;
}

11. 构建与测试

# CMake 一键构建 + 测试
cmake -S . -B build && cmake --build build --target check

# 基准测试
cmake --build build --target bench

详见 构建指南