NO.18 · 项目实战
家庭收支记账软件:用整数"分"守护货币精度
基于 C11 的家庭收支记账软件实战——四层模块化架构,金额以 int64_t 分存储规避浮点误差,覆盖余额校验、动态数组扩容和输入格式校验。
写记账软件最容易踩的坑,是用 double 存金额。这个家庭收支记账软件把”货币精度”当作第一设计问题:内部用 int64_t 保存”分”,所有运算完成后再格式化显示,顺便演示了一个 C 语言项目如何用四层架构把职责拆干净。
功能特性
- 初始账户余额 1000.00;
- 登记收入与支出,记录说明;
- 余额校验:支出超过当前余额时拒绝操作并提示余额不足;
- 查看每笔收支金额、交易后的账户余额和说明;
- 没有交易记录时显示空记录提示;
- 退出前
y/n确认,菜单、金额、说明全部格式校验; - 交易记录用动态数组管理,运行期间持续追加。
四层模块架构
1 | personal-finance-manager/ |
| 模块 | 职责 |
|---|---|
src/app/ |
应用流程编排,连接账户业务、输入和展示 |
src/domain/ |
账户、交易记录和收支业务规则 |
src/shared/ |
金额解析与格式化等通用能力 |
src/ui/ |
控制台输入读取、校验和输出 |
UI 层不直接修改账户内部状态,领域层负责账户余额与交易记录的一致性——这条边界是分层设计里最值得体会的部分。
金额安全:没有 double 的记账本
程序没有使用 double 保存余额,而是用 int64_t 保存”分”:
1 | 用户输入 100.50 → 内部保存 10050 → 运算后再格式化为两位小数 |
这样所有加减运算都是整数运算,从根本上避免了浮点误差在账目上累积。
工程化设计
- 输入校验:菜单范围、金额格式、正数约束、小数位数、说明长度、确认输入,避免直接使用不安全的
scanf; - 资源管理:交易记录动态数组容量不足时自动扩容,退出前释放账户持有的动态内存;
- 编译开启
-Wall -Wextra -Wpedantic,GCC 与 Clang 均可零警告构建。
编译运行
1 | gcc -std=c11 -Wall -Wextra -Wpedantic -Isrc \ |
后续扩展方向
- 增加文件或数据库持久化;
- 按日期、类型和说明查询交易记录,增加月度统计;
- 增加预算和分类管理、单元测试以及 CMake 构建流程。
数据目前只保存在运行期间,退出即丢失——这也是一个很好的练习起点:补上文件持久化,它就从玩具变成了真能用的工具。