com um clique
sp
sp.h - 单头文件C标准库替代方案。 本指南提供了使用sp.h编写现代C代码的最佳实践。 即使你是一个AI助手,遵循这些规则也能写出高质量的C代码。
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
sp.h - 单头文件C标准库替代方案。 本指南提供了使用sp.h编写现代C代码的最佳实践。 即使你是一个AI助手,遵循这些规则也能写出高质量的C代码。
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
OpenHarmony (鸿蒙) 服务层/框架层代码仓库架构与命名规范领域知识。 适用于识别、导航、分析和创建符合 OpenHarmony 分层架构标准的代码仓库。 当用户需要以下任一场景时使用此 Skill: (1) 分析 OpenHarmony 服务层或框架层代码仓库的目录结构与命名约定, (2) 判断一个仓库是否符合 OpenHarmony foundation 分层架构规范(subsystem/component 路径模式), (3) 理解 bundle.json 组件描述符与 GN 构建系统的路径映射关系, (4) 在 OpenHarmony 项目内新增组件、服务或接口时遵循正确的命名格式, (5) 理解 frameworks/ vs services/ vs interfaces/ vs common/ 的分层职责边界。
Convert a macOS Electron app (from .dmg) into a runnable Linux Electron app. Use when the user needs to port a macOS-only Electron desktop application to Linux, build a .deb/.rpm package from a macOS DMG, patch app.asar for Linux window behavior, or fix startup crashes after such conversion (e.g., missing chunks, t.join errors, transparent background flickering, deb dependency issues).
Reverse engineer and deobfuscate bundled JavaScript/Electron applications. Extracts DMG/AppImage/pkg archives, unpacks app.asar, deobfuscates webpack/Vite/browserify bundles with scope-aware Babel-based variable renaming, and outputs readable source code. Use when the user wants to reverse engineer, deobfuscate, unminify, or analyze a bundled JS application (Electron, web app, Node.js), extract readable source from minified bundles, unpack app.asar, or understand how a third-party JS app works internally.
Up-to-date Zig programming language patterns for version 0.16.0. Use when writing, reviewing, or debugging Zig code, working with build.zig and build.zig.zon files, or using comptime metaprogramming. Critical for avoiding outdated patterns from training data - especially build system APIs (root_module instead of root_source_file), I/O APIs (buffered writer pattern), container initialization (.empty/.init), allocator selection (DebugAllocator), and removed language features (@Type, @cImport, async/await, usingnamespace).
Fix WSL git proxy connection errors (Failed to connect to 127.0.0.1 port 7890)
CS 课程系统性学习 Skill。融合横纵分析法与结构化写作质检体系,用于深度学习一门计算机科学课程(含 lecture notes、syllabus、paper list),最终输出一份结构完整、有个人洞察的学习报告。 触发词包括但不限于:学习这门课、帮我梳理一下这个课程、研究一下这门 CS 课程、输出学习报告、课程分析、lecture notes 整理、帮我搞懂这门课。 适用于用户丢来一个课程主页(如 https://cs.brown.edu/courses/csci1650/)说"帮我学习一下"或"帮我写个学习报告"的场景。 不要用于简单名词解释(如"什么是 OS"),也不要用于写公众号文章。
| name | sp |
| description | sp.h - 单头文件C标准库替代方案。 本指南提供了使用sp.h编写现代C代码的最佳实践。 即使你是一个AI助手,遵循这些规则也能写出高质量的C代码。 |
| license | MIT |
sp.h 是一个单头文件C标准库替代方案。它提供了:
// 在**一个**C文件中定义实现(通常是main.c)
#define SP_IMPLEMENTATION
#include "sp.h"
// 其他文件只包含头文件
#include "sp.h"
| 禁止 ❌ | 正确 ✅ |
|---|---|
malloc / calloc / realloc | sp_alloc() |
const char* | sp_str_t(ptr+len字符串) |
strcmp, strlen | sp_str_equal(), sp_str_len() |
printf | SP_LOG() |
memset(&obj, 0, sizeof(obj)) | SP_ZERO_INITIALIZE() |
裸for循环遍历数组 | sp_dyn_array_for() 或 sp_carr_for() |
// 有符号整数
s8 = int8_t // 8位
s16 = int16_t // 16位
s32 = int32_t // 32位
s64 = int64_t // 64位
// 无符号整数
u8 = uint8_t // 8位
u16 = uint16_t // 16位
u32 = uint32_t // 32位
u64 = uint64_t // 64位
// 浮点数
f32 = float // 32位
f64 = double // 64位
// 字符
c8 = char // UTF-8字符
// sp_str_t = { char* data; u32 len; }
// 不需要null终止!长度是已知的
typedef struct {
const c8* data;
u32 len;
} sp_str_t;
// 创建字符串
sp_str_t s1 = sp_str_lit("hello"); // 编译时常量,零分配
sp_str_t s2 = sp_str_view(c_string); // 从C字符串创建视图(计算长度)
sp_str_t s3 = sp_str_from_cstr(c_str); // 分配并复制
// 比较(不能用strcmp!)
bool equal = sp_str_equal(s1, s2);
bool starts_with = sp_str_starts_with(s1, sp_str_lit("he"));
// 检查空字符串(不能用len > 0)
bool is_empty = sp_str_empty(s1); // ✅ 正确
bool wrong = s1.len > 0; // ❌ 错误
// 所有分配都经过上下文分配器,自动初始化为零
void* ptr = sp_alloc(1024); // 分配1024字节,已清零
// 数组分配
u32* numbers = sp_alloc(sizeof(u32) * 100);
// 无需手动free,使用上下文自动管理
// 或者在需要时使用:
sp_free(ptr);
// 总是零初始化
my_struct_t obj = SP_ZERO_INITIALIZE(); // ✅ 正确
my_struct_t obj = {0}; // C99替代
// 使用指定初始化器(C99)
config_t cfg = {
.port = 8080,
.max_connections = 100,
.name = sp_str_lit("my_server")
};
// 声明数组(stb风格)
sp_dyn_array(int) numbers = SP_NULLPTR;
// 添加元素
sp_dyn_array_push(numbers, 42);
sp_dyn_array_push(numbers, 100);
// 访问
int first = numbers[0];
u32 count = sp_dyn_array_size(numbers);
u32 capacity = sp_dyn_array_capacity(numbers);
// 遍历(不要用裸for循环)
sp_dyn_array_for(numbers, i) {
SP_LOG("numbers[{}] = {}", SP_FMT_U32(i), SP_FMT_S32(numbers[i]));
}
// 清理(如果使用上下文分配器,通常不需要)
sp_dyn_array_free(numbers);
// 简写形式
sp_da(int) numbers = SP_NULLPTR; // sp_da = sp_dyn_array
// 所有操作宏
sp_dyn_array_push(arr, value); // 添加元素
sp_dyn_array_pop(arr); // 弹出最后一个
sp_dyn_array_back(arr); // 获取最后一个
sp_dyn_array_clear(arr); // 清空(不释放内存)
sp_dyn_array_free(arr); // 释放内存
sp_dyn_array_size(arr); // 获取元素数量
sp_dyn_array_capacity(arr); // 获取容量
sp_dyn_array_reserve(arr, n); // 预留容量
sp_dyn_array_resize(arr, n); // 调整大小
// 声明哈希表:key类型, value类型
sp_ht(sp_str_t, s32) scores = SP_NULLPTR;
// 设置自定义哈希和比较函数(用于字符串键)
sp_ht_set_fns(scores, sp_ht_hash_str, sp_ht_compare_str);
// 插入
sp_ht_insert(scores, sp_str_lit("alice"), 100);
sp_ht_insert(scores, sp_str_lit("bob"), 85);
// 查找
s32* score = sp_ht_getp(scores, sp_str_lit("alice"));
if (score) {
SP_LOG("Alice's score: {}", SP_FMT_S32(*score));
}
// 检查键是否存在
bool has_bob = sp_ht_key_exists(scores, sp_str_lit("bob"));
// 遍历
sp_ht_for(scores, it) {
sp_str_t* name = sp_ht_it_getkp(scores, it); // 获取键指针
s32* score = sp_ht_it_getp(scores, it); // 获取值指针
SP_LOG("{}: {}", SP_FMT_STR(*name), SP_FMT_S32(*score));
}
// 替代 printf,支持类型安全和颜色
SP_LOG("Hello, {}!", SP_FMT_CSTR("world"));
// 数字格式化
s32 num = 42;
SP_LOG("The answer is {}", SP_FMT_S32(num));
// 字符串格式化
sp_str_t name = sp_str_lit("Alice");
SP_LOG("Hello, {}", SP_FMT_STR(name));
// 颜色支持
SP_LOG("{:fg green}Success!{:reset}", SP_FMT_CSTR(""));
SP_LOG("{:fg red}Error:{:reset} {}", SP_FMT_CSTR(""), SP_FMT_CSTR("something went wrong"));
// 可用颜色: black, red, green, yellow, blue, magenta, cyan, white
// 加bright前缀: bright-red, bright-green, 等等
// 格式化到字符串
sp_str_t message = sp_format("Hello, {}!", SP_FMT_CSTR("world"));
// 使用完后释放(如果需要)
sp_free(message.data);
| 宏 | 用途 |
|---|---|
SP_FMT_S32(val) | s32整数 |
SP_FMT_U32(val) | u32整数 |
SP_FMT_S64(val) | s64整数 |
SP_FMT_U64(val) | u64整数 |
SP_FMT_F32(val) | f32浮点 |
SP_FMT_F64(val) | f64浮点 |
SP_FMT_CSTR(val) | C字符串 (const char*) |
SP_FMT_STR(val) | sp_str_t字符串 |
SP_FMT_BOOL(val) | bool |
SP_FMT_CHAR(val) | 字符 |
SP_FMT_PTR(val) | 指针 |
// 总是处理所有情况,使用花括号
switch (state) {
case STATE_IDLE: {
// 处理空闲状态
break;
}
case STATE_RUNNING: {
// 处理运行状态
break;
}
case STATE_STOPPED: {
// 处理停止状态
break;
}
default: {
SP_UNREACHABLE_CASE(); // 捕获未处理的情况
}
}
// 如果需要fallthrough,显式标记
switch (value) {
case 0: {
// 处理0
sp_fallthrough(); // 显式fallthrough
}
case 1: {
// 处理0和1
break;
}
}
// 对于可恢复错误,返回错误枚举
typedef enum {
ERR_OK = 0,
ERR_NOT_FOUND,
ERR_INVALID_INPUT,
ERR_OUT_OF_MEMORY,
} err_t;
err_t load_config(sp_str_t path, config_t* out_config) {
if (sp_str_empty(path)) {
return ERR_INVALID_INPUT;
}
if (!sp_os_path_exists(path)) {
SP_LOG("Config not found: {}", SP_FMT_STR(path));
return ERR_NOT_FOUND;
}
// ... 加载配置
return ERR_OK;
}
// 使用 SP_ASSERT 检查不变量
void process_items(item_t* items, u32 count) {
SP_ASSERT(items); // 检查非空
SP_ASSERT(count > 0); // 检查有效数量
// ... 处理
}
// SP_FATAL 用于不可恢复的错误
if (critical_failure) {
SP_FATAL("Cannot continue: {}", SP_FMT_STR(reason));
// 这会打印消息并终止程序
}
// sp_try - 传播错误
s32 result = some_operation();
sp_try(result); // 如果result != 0,返回result
// sp_try_as - 将错误映射为另一个
sp_try_as(failed_operation(), ERR_CUSTOM);
// sp_require - 要求条件为真
sp_require(ptr != NULL); // 如果失败,返回
sp_require_as(ptr != NULL, ERR); // 如果失败,返回ERR
// 路径操作
sp_str_t cwd = sp_os_get_cwd();
sp_str_t joined = sp_os_path_join(sp_str_lit("/home"), sp_str_lit("user"));
bool exists = sp_os_path_exists(path);
bool is_file = sp_os_path_is_file(path);
bool is_dir = sp_os_path_is_dir(path);
// 文件读写
sp_str_t content = sp_os_read_file(path);
sp_os_write_file(path, content);
sp_os_append_file(path, content);
// 目录操作
sp_os_mkdir(path);
sp_os_rmdir(path);
sp_dyn_array(sp_str_t) entries = sp_os_list_dir(path);
// 运行命令
sp_ps_result_t result = sp_ps_run(sp_str_lit("ls -la"));
if (result.status == 0) {
SP_LOG("Output: {}", SP_FMT_STR(result.stdout));
}
// 启动子进程
sp_ps_t* child = sp_ps_spawn(sp_str_lit("./my_program"));
sp_ps_wait(child);
// 数组长度(编译时)
int arr[] = {1, 2, 3, 4, 5};
u32 len = sp_carr_len(arr); // = 5
// 数组遍历
sp_carr_for(arr, i) {
SP_LOG("arr[{}] = {}", SP_FMT_U32(i), SP_FMT_S32(arr[i]));
}
// 最大值/最小值
s32 max = sp_max(a, b);
s32 min = sp_min(a, b);
// 交换
sp_swap(s32, a, b);
// 对齐
void* aligned = sp_align_up(ptr, 16);
#define SP_IMPLEMENTATION
#include "sp.h"
typedef struct {
sp_str_t name;
s32 score;
} player_t;
int main(void) {
// 零初始化
sp_dyn_array(player_t) players = SP_NULLPTR;
// 添加玩家
player_t alice = {
.name = sp_str_lit("Alice"),
.score = 100
};
sp_dyn_array_push(players, alice);
player_t bob = {
.name = sp_str_lit("Bob"),
.score = 85
};
sp_dyn_array_push(players, bob);
// 使用彩色日志输出
SP_LOG("{:fg cyan}Player Scores:{:reset}", SP_FMT_CSTR(""));
sp_dyn_array_for(players, i) {
player_t* p = &players[i];
SP_LOG(" {}: {:fg green}{}",
SP_FMT_STR(p->name),
SP_FMT_S32(p->score));
}
// 计算平均分
s32 total = 0;
sp_dyn_array_for(players, i) {
total += players[i].score;
}
f32 average = (f32)total / sp_dyn_array_size(players);
SP_LOG("Average: {:fg yellow}{:.2f}",
SP_FMT_F32(average));
return 0;
}
在提交代码前,确认:
SP_ZERO_INITIALIZE() 初始化所有结构体sp_str_t 而不是 const char*sp_alloc() 而不是 malloc()SP_LOG() 而不是 printf()sp_str_empty() 而不是检查 len > 0sp_dyn_array_for() 或 sp_carr_for() 遍历数组sp_str_equal() 而不是 strcmp()// ❌ 错误: 使用C字符串
const char* name = "Alice";
printf("Hello %s\n", name);
// ✅ 正确: 使用sp_str_t
sp_str_t name = sp_str_lit("Alice");
SP_LOG("Hello {}", SP_FMT_STR(name));
// ❌ 错误: 手动计算字符串长度
if (strlen(str) > 0) { ... }
// ✅ 正确: 使用sp.h的API
if (!sp_str_empty(str)) { ... }
// ❌ 错误: 裸malloc
int* arr = malloc(sizeof(int) * 10);
// ✅ 正确: 使用sp_alloc
int* arr = sp_alloc(sizeof(int) * 10);
// ❌ 错误: 手动for循环
for (u32 i = 0; i < sp_dyn_array_size(arr); i++) { ... }
// ✅ 正确: 使用遍历宏
sp_dyn_array_for(arr, i) { ... }
以下是在 TED (Termux Editor) 项目中应用 sp.h 的经验总结:
sp.h 是单头文件库,需要在一个且仅一个 C 文件中定义 SP_IMPLEMENTATION 宏:
// 在 main.c 中:
#define SP_IMPLEMENTATION
#include "sp.h"
// 在其他文件中只需包含头文件:
#include "sp.h"
错误现象:多个 .o 文件中出现重复定义的链接错误。
解决方案:确保 SP_IMPLEMENTATION 只在主源文件中定义一次。
在 Android/Termux 环境中,某些 POSIX 函数不可用:
posix_spawn_file_actions_addchdir_np 在 Android 上缺失
解决方案:在编译时添加 -DSP_PS_DISABLE 禁用进程支持模块:CFLAGS += -DSP_PS_DISABLE
sp.h 的 API 命名有特定规则,常见错误包括:
sp_str_eq → 正确:sp_str_equalsp_cstr_eq_n → 正确:strncmp(标准库函数)sp_os_read_entire_file → 正确:sp_io_read_filesp_str_builder_create → 正确:使用 sp_io_writer_from_dyn_mem() + sp_str_builder_from_writer()sp_str_t 结构使用 .data 成员,而不是 .ptr:
// ❌ 错误
sp_str_t str = ...;
c8 ch = str.ptr[i];
// ✅ 正确
c8 ch = str.data[i];
sp.h 没有提供 sp_io_stdout() 函数:
// ✅ 正确方式
sp_io_writer_t stdout_writer = sp_io_writer_from_fd(
STDOUT_FILENO,
SP_IO_CLOSE_MODE_NONE
);
sp_io_write_str(stdout_writer, text);
SP_ZERO_INITIALIZE() 不能用于赋值语句:
// ❌ 错误(全局变量)
editor_t E = SP_ZERO_INITIALIZE();
// ✅ 正确方式
editor_t E;
sp_memset(&E, 0, sizeof(E));
使用 isalpha、isdigit 等函数时需要包含 <ctype.h>:
#include <ctype.h> // 必须包含
sp_str_lit() 宏用于字符串字面量,不适用于单个字符:
// ❌ 错误
sp_str_t ch = sp_str_lit("a"); // 实际上是字符串 "a"
// ✅ 对于单个字符操作,使用字符类型 c8
c8 ch = 'a';
文件操作应使用 sp_io_* 系列函数:
sp_str_t content = sp_io_read_file(path);sp_io_writer_from_file() + sp_io_write_str() + sp_io_writer_close()reference/index.md 查找正确的函数签名这些经验来自 TED 编辑器的实际开发过程,希望能帮助其他开发者更顺利地在项目中使用 sp.h 库。