plugins/languages/c/skills/error/SKILL.md
C error-handling conventions: errno/perror/strerror_r usage, goto cleanup resource pattern, safe string operations (snprintf, strncpy + NUL terminator, memcpy with length checks), explicit error-code enums, [[nodiscard]] for critical returns, and integer overflow guards (size multiplication, INT_MAX limits). Use when designing error paths, propagating failures across layers, or hardening against silently-ignored returns. Triggers on "errno", "goto cleanup", "strcpy 替代", "snprintf", "整数溢出", "错误码设计", "defensive coding", "[[nodiscard]]".
npx skillsauth add lazygophers/ccplugin c-errorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
goto cleanup 集中释放;free(NULL) 安全无需判空。errno 在可能被覆盖前保存到本地变量。strcpy / sprintf / strcat / gets;用 snprintf / strncpy + NUL / memcpy。snprintf 返回值需同时判断 < 0(编码错误)和 >= sizeof(dest)(截断)。[[nodiscard]](C23)或 __attribute__((warn_unused_result))。signal();信号注册一律 sigaction(细节见 c-posix)。FILE *f = fopen(path, "r");
if (!f) { perror("fopen"); return -1; }
// 保存 errno,因为后续清理可能覆盖
int saved = errno;
cleanup();
errno = saved;
// 线程安全
char buf[256];
strerror_r(errno, buf, sizeof buf);
fprintf(stderr, "fail: %s\n", buf);
goto cleanup 标准模板int process(const char *in_path, const char *out_path) {
int rc = -1;
FILE *in = NULL, *out = NULL;
char *buf = NULL;
if (!(in = fopen(in_path, "r"))) { perror("open in"); goto out; }
if (!(out = fopen(out_path, "w"))) { perror("open out"); goto out; }
if (!(buf = malloc(BUFSZ))) { perror("malloc"); goto out; }
size_t n;
while ((n = fread(buf, 1, BUFSZ, in)) > 0)
if (fwrite(buf, 1, n, out) != n) { perror("write"); goto out; }
if (ferror(in)) { perror("read"); goto out; }
rc = 0;
out:
free(buf);
if (out) fclose(out);
if (in) fclose(in);
return rc;
}
typedef enum {
ERR_OK = 0,
ERR_NULL_PTR = -1,
ERR_NO_MEM = -2,
ERR_INVALID_ARG = -3,
ERR_IO = -4,
ERR_OVERFLOW = -5,
} ErrorCode;
#if __STDC_VERSION__ >= 202311L
[[nodiscard]]
#else
__attribute__((warn_unused_result))
#endif
ErrorCode do_thing(const char *in, char *out, size_t cap);
char dst[64];
// snprintf — 同时判断错误与截断
int n = snprintf(dst, sizeof dst, "id=%d name=%s", id, name);
if (n < 0 || (size_t)n >= sizeof dst) return ERR_OVERFLOW;
// strncpy 必须手动补 NUL
strncpy(dst, src, sizeof dst - 1);
dst[sizeof dst - 1] = '\0';
// memcpy(已知精确长度时最快)
size_t len = strlen(src);
if (len >= sizeof dst) return ERR_OVERFLOW;
memcpy(dst, src, len + 1);
C11 Annex K(strcpy_s 等 _s 系列)仅可选实现,不可移植,不推荐。
#include <stdckdint.h> // C23:标准的 ckd_add/ckd_mul/ckd_sub
// C23
size_t total;
if (ckd_mul(&total, n, sizeof(T))) return ERR_OVERFLOW;
// 兼容写法
static inline bool safe_mul(size_t a, size_t b, size_t *r) {
if (a && b > SIZE_MAX / a) return false;
*r = a * b; return true;
}
// GCC/Clang 内置(已有 10+ 年)
size_t r;
if (__builtin_mul_overflow(n, sizeof(T), &r)) return ERR_OVERFLOW;
perror / strerror_r 出错点。(void) cast 必须有注释解释为何忽略。(void) + 注释)goto cleanupsnprintf 同时判断错误 + 截断ckd_* / __builtin_*_overflow / 手写守卫[[nodiscard]] 或 warn_unused_result<stdckdint.h> 草案 — https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf__builtin_*_overflow — https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.htmlstrerror_r — https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.htmldevelopment
Go 数据库规范——GORM Model 命名 ModelXxx、表名单数、枚举 uint8 + 常量、索引 idx_ 前缀 + deleted_at leading column、禁 time.Time 统一 int64 unix、禁指针/nullable 字段、TEXT/BLOB/JSON 禁 default、AutoMigrate 禁改主键。设计 DB model、写 GORM tag、建索引、做 migration 审查时触发。
development
Go HTTP API 规范——响应始终 200 + body code 字段、路由 /api/* 全 POST 单段 <Action><Model>、中间件逐路由注册禁 Group(prefix,mw...)、handler 仅返回 (rsp,error)、认证走 header。设计 HTTP API、写路由/handler/中间件时触发。
development
Go 项目结构规范——三层架构(API → Impl → State)、全局状态模式、internal/ 私有包、cmd/ 仅 main.go、go.work 多模块、禁止 Repository 接口和 DI 容器、struct 公共字段开头全 omitempty、handler var rsp 顶声明、禁 legacy migration。设计项目骨架、新建目录、组织包、做架构评审时触发。
development
Go 命名规范——Id/Uid 字段(非 ID)、IsActive/HasMFA 布尔前缀、CreatedAt 时间字段、接收者统一用 p、包名全小写无下划线、泛型类型参数描述性命名、集合字段 xxx_list 禁 xxxs 复数、Enum 0 值 XxxNil 禁 Unknown、禁 Status 统一 State、Set/Update 语义区分。定义结构体字段、函数、变量、包、接收者名、泛型、枚举时触发。