跳到正文
XiaoGao 的个人博客

NO.18 · 项目实战

家庭收支记账软件:用整数"分"守护货币精度

基于 C11 的家庭收支记账软件实战——四层模块化架构,金额以 int64_t 分存储规避浮点误差,覆盖余额校验、动态数组扩容和输入格式校验。

发布 DATE
字数 WORDS
712
阅读 READ
2 分钟

写记账软件最容易踩的坑,是用 double 存金额。这个家庭收支记账软件把”货币精度”当作第一设计问题:内部用 int64_t 保存”分”,所有运算完成后再格式化显示,顺便演示了一个 C 语言项目如何用四层架构把职责拆干净。

功能特性

  • 初始账户余额 1000.00;
  • 登记收入与支出,记录说明;
  • 余额校验:支出超过当前余额时拒绝操作并提示余额不足;
  • 查看每笔收支金额、交易后的账户余额和说明;
  • 没有交易记录时显示空记录提示;
  • 退出前 y/n 确认,菜单、金额、说明全部格式校验;
  • 交易记录用动态数组管理,运行期间持续追加。

四层模块架构

1
2
3
4
5
6
7
8
personal-finance-manager/
├── src/
│ ├── main.c # 程序入口
│ ├── app/ # 应用层:流程编排
│ ├── domain/ # 领域层:账户与业务规则
│ ├── shared/ # 共享层:金额解析与格式化
│ └── ui/ # 界面层:输入读取与展示
└── docs/ # 需求文档
模块 职责
src/app/ 应用流程编排,连接账户业务、输入和展示
src/domain/ 账户、交易记录和收支业务规则
src/shared/ 金额解析与格式化等通用能力
src/ui/ 控制台输入读取、校验和输出

UI 层不直接修改账户内部状态,领域层负责账户余额与交易记录的一致性——这条边界是分层设计里最值得体会的部分。

金额安全:没有 double 的记账本

程序没有使用 double 保存余额,而是用 int64_t 保存”分”:

1
用户输入 100.50  →  内部保存 10050  →  运算后再格式化为两位小数

这样所有加减运算都是整数运算,从根本上避免了浮点误差在账目上累积。

工程化设计

  • 输入校验:菜单范围、金额格式、正数约束、小数位数、说明长度、确认输入,避免直接使用不安全的 scanf;
  • 资源管理:交易记录动态数组容量不足时自动扩容,退出前释放账户持有的动态内存;
  • 编译开启 -Wall -Wextra -Wpedantic,GCC 与 Clang 均可零警告构建。

编译运行

1
2
3
4
5
gcc -std=c11 -Wall -Wextra -Wpedantic -Isrc \
src/main.c src/app/finance_app.c \
src/domain/finance_account.c src/shared/money.c \
src/ui/console_input.c src/ui/console_view.c \
-o finance-manager

后续扩展方向

  • 增加文件或数据库持久化;
  • 按日期、类型和说明查询交易记录,增加月度统计;
  • 增加预算和分类管理、单元测试以及 CMake 构建流程。

数据目前只保存在运行期间,退出即丢失——这也是一个很好的练习起点:补上文件持久化,它就从玩具变成了真能用的工具。

仓库

    ↑↓ 选择↵ 打开>theme · >random · >top · >archive