MENTAL MODEL / 心智模型
先建立正确的程序直觉
值有类型,内存有边界,资源有唯一生命周期;每个指针都要回答“指向哪里、能用多久、谁来释放”。
Mathematics · Computational Science · Engineering · Personal Knowledge Systems
切换会载入对应网站形态,并更换导航、首页结构、内容密度、页面网格与阅读路径;当前路径及其他查询参数会保留。
预览卡展示真实构图语言;切换后会同步改变布局密度、卡片几何、按钮、边框、HUD、背景纹样与动效节奏。
0% 会停止装饰动画并隐藏粒子;系统开启“减少动态效果”时始终优先静态显示。
0% 会移除上层面板遮罩,100% 为完全不透明;文字和控件始终保持清晰。
系统已启用减少透明效果:背景模糊会关闭,但仍采用你设置的面板不透明度。
LANG-01 · PROGRAMMING WORKBENCH
从数据布局、指针和资源生命周期理解程序如何贴近机器运行。
以清晰所有权、编译器警告和动态检查为护栏,建立能编译、能调试、能验证的 C 工程工作流。
MENTAL MODEL / 心智模型
值有类型,内存有边界,资源有唯一生命周期;每个指针都要回答“指向哪里、能用多久、谁来释放”。
CODE REPOSITORY / 代码仓库锚点
c-project/
├─ CMakeLists.txt
├─ include/app/math_utils.h
├─ src/main.c
├─ src/math_utils.c
└─ tests/test_math_utils.cQUICK START / 第一条命令
cc -std=c17 -Wall -Wextra -Wpedantic -Wconversion -g src/main.c -o build/appLEARNING ROUTE / 学习路径
每个节点都包含可复制示例、判断原则与常见陷阱。
掌握边界、返回值和资源释放,比记住更多函数更重要。
size_t count = sizeof values / sizeof values[0];
for (size_t i = 0; i < count; ++i) {
printf("%zu: %d\n", i, values[i]);
}只适用于当前作用域中的真实数组;数组传给函数后会退化为指针。
int written = snprintf(buffer, sizeof buffer, "%s-%d", name, id);
if (written < 0 || (size_t)written >= sizeof buffer) {
fputs("output truncated\n", stderr);
}不要假设目标缓冲区一定足够;始终检查 snprintf 返回值。
FILE *file = fopen(path, "rb");
if (file == NULL) return false;
bool ok = read_payload(file);
if (fclose(file) != 0) ok = false;
return ok;打开资源后,所有路径都必须释放;复杂函数可用 cleanup 标签统一回收。
头文件公开契约,源文件隐藏实现,测试通过公开接口验证行为。
#ifndef APP_MATH_UTILS_H
#define APP_MATH_UTILS_H
#include <stddef.h>
double mean(const double *values, size_t count);
#endif公开声明、必要类型和文档;不要在头文件中定义可变全局对象。
cmake_minimum_required(VERSION 3.20)
project(memory_lab LANGUAGES C)
add_library(core src/math_utils.c)
target_include_directories(core PUBLIC include)
add_executable(app src/main.c)
target_link_libraries(app PRIVATE core)用 target 表达依赖、包含路径和编译特性,避免堆积全局选项。
把警告视为工程反馈,用 Debug 和 Release 两类构建分离排错与性能。
cc -std=c17 -Wall -Wextra -Wpedantic -Wconversion -g src/main.c -o build/app先解决警告,再讨论优化;不同编译器可追加各自的诊断选项。
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build --parallel
ctest --test-dir build --output-on-failure多配置生成器的 Debug/Release 通常在 --build 阶段用 --config 选择。
cc -fsanitize=address,undefined -fno-omit-frame-pointer -g src/main.c -o build/app && ./build/appAddressSanitizer/UBSan 可暴露越界、释放后使用和多类未定义行为。
锁定版本、缩小依赖面,并让构建脚本描述依赖来源。
{
"name": "memory-lab",
"version-string": "0.1.0",
"dependencies": ["cjson"]
}把 vcpkg.json 纳入版本控制;使用 baseline 锁定可复现版本。
find_package(cJSON CONFIG REQUIRED)
target_link_libraries(app PRIVATE cjson)链接到导入 target,让 include、库和平台差异跟随依赖声明。
先保留失败现场,再缩小输入,最后把复现案例固定成测试。
gdb ./build/app
break main
run
bt
frame 0
print variable程序应带 -g;崩溃后先看 bt 调用栈,不要急着改代码。
#include <assert.h>
#include <math.h>
int main(void) {
assert(fabs(mean((double[]){2, 4, 6}, 3) - 4.0) < 1e-12);
return 0;
}覆盖正常值、空输入、边界长度和错误返回;生产校验不要依赖可被 NDEBUG 关闭的 assert。
错误发生位置不一定是根因位置,按编译、链接、运行三个阶段判断。
检查:实现文件是否加入 target、声明与定义签名是否一致、库顺序与 C/C++ ABI 是否匹配。这是链接阶段错误;仅增加 include 不会把实现链接进程序。
保留 core/调用栈 → 用 ASan 重跑 → 检查空指针、越界、释放后使用与栈生命周期。不要只在崩溃行加空判断;追踪指针从创建到失效的全过程。
检查未初始化读取、整数溢出、越界、别名与未定义行为;启用 -Wall -Wextra -Wconversion 和 UBSan。优化常让潜伏的未定义行为以不同方式显现。
不信任长度、索引、格式字符串和外部文件;所有转换都要检查范围。
char *end = NULL;
errno = 0;
long value = strtol(text, &end, 10);
if (errno != 0 || end == text || *end != '\0' || value < 0 || value > INT_MAX) {
return false;
}atoi 无法可靠区分错误;strtol 需要同时检查 errno、尾指针和业务范围。
if (count > SIZE_MAX / sizeof *items) return NULL;
items = calloc(count, sizeof *items);先验证乘法不会溢出,再检查分配结果。
禁止 gets;外部文本不可直接作为 printf 格式串;敏感信息不写日志;依赖和编译器定期更新。编译器加固是补充,不能替代边界检查和明确所有权。
尝试更短的关键词,或者清空分类后重新搜索。
PRIVATE CODE VAULT / 私人代码仓库
把 C 的练习、工程片段、算法伪代码、验证说明与历史版本保存在登录后的私人空间。
游客只看到这个说明,不会收到、缓存或渲染任何私人源码。
REFERENCE SOURCES / 官方与高质量资料
先在本站找回路径,再进入原始文档确认版本、平台和完整前置条件。
DELIVERY LOOP / 练习闭环