C语言实现简易个人财务管理系统

Ryan
Ryan
发布时间:2026-10-13| 6人浏览过
原创

本文展示如何使用C语言编写一个基于控制台的个人财务管理程序。系统通过结构体记录交易详情,支持录入收支、按人物和时间维度统计余额,提供基础的金融数据管理功能。

程序目标与设计思路

本程序旨在模拟个人财务管理工作流程,以财政年度为统计周期,追踪指定人员在特定年份内的总收入、总支出及结余情况。设计核心在于如何处理多笔交易的聚合计算,具体思路如下:

  1. 数据结构定义:每一笔交易视为一条独立记录,需包含交易日期(年/月/日)、操作人姓名、收入金额和支出金额。使用结构体 struct deal 封装这些信息,内部嵌套日期结构体 struct Date。
  2. 容量规划:程序预设最大交易笔数为50笔(由宏 SIZE 50 定义),通过定长数组存储历史数据。
  3. 数据统计逻辑:由于同一人在同一年可能有收支多笔,输出时需先对数据进行排序(优先按姓名字母序,其次按年份顺序),随后遍历数组,提取相同姓名且同年度的记录进行累加求和。

环境准备

开发本程序只需标准 C 语言编译环境(如 GCC、Clang 或 Visual Studio)。主要依赖库包括:

  • <stdio.h>:用于控制台输入输出(printf, scanf)。
  • <stdlib.h>:用于数值转换函数(atof)及程序退出(exit)。
  • <string.h>:用于字符串比较(strcmp)和拷贝(strcpy)。

第1步:定义核心数据结构

目的:建立存储交易信息的底层模型。

操作:在源码中定义日期结构和交易结构。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 50

// 日期结构体
struct Date {
    int year, month, day;
};

// 交易记录结构体
struct deal {
    struct Date date;     // 交易时间
    char name[12];        // 操作人姓名
    double earning;       // 收入金额
    double payout;        // 支出金额
};

第2步:实现主循环与菜单交互

目的:提供用户操作入口,维持程序运行状态。

操作:main 函数初始化交易数组,通过 while(1) 死循环展示菜单并接受用户指令。

int main() {
    struct deal person[SIZE] = {0}; // 初始化所有金额为0
    char key;
    int i = 0; // 当前已录入的交易索引

    while(1) {
        Menu();             // 显示菜单
        key = getchar();    // 获取用户选择
        printf("\n");       
        switch(key) {
            case '1':
                InputOneDeal(person + i); // 录入新交易
                i++;                    // 索引增加
                break;
            case '2':
                PrintOneDeal(person);     // 打印报表
                break;
            case '3':
                exit(0);                  // 退出程序
            default:
                break;
        }
    }
    return 0;
}

void Menu() {
    printf("1. Input one deal\n");
    printf("2. Print one year balance\n");
    printf("3. Exit\n");
    printf("Please select(1-3):");
}

第3步:实现单笔交易录入

目的:收集用户的交易数据并验证格式。

操作:InputOneDeal 函数提示用户输入日期、姓名及金额。对于金额,通过首字符判断正负(‘-’代表支出),分别填充至 earning 或 payout 字段。

void InputOneDeal(struct deal *per) {
    char s[10];
    printf("Date(YYYY-MM-DD): ");
    scanf("%d-%d-%d", &per->date.year, &per->date.month, &per->date.day);
    
    printf("name: ");
    scanf("%9s", s); // 限制读取长度防止溢出
    strncpy(per->name, s, sizeof(per->name));
    per->name[sizeof(per->name)-1] = '\0';
    
    printf("deal(+/-): ");
    scanf("%9s", s);
    
    // 解析金额:以'-'开头视为支出,否则视为收入
    if(s[0] == '-') {
        per->payout = atof(s);
        per->earning = 0;
    } else {
        per->earning = atof(s);
        per->payout = 0;
    }
}

第4步:实现排序与统计打印

目的:将杂乱的交易日志整理为清晰的对账单,按人名分组,按年度汇总。

操作:

  1. 排序:SortPerson 函数使用简单的选择排序算法,将 person 数组的有效记录拷贝到指针数组 per_sort 中,并按姓名字符串升序、年份升序排列,便于后续连续处理相同用户的数据。
  2. 遍历统计:PrintOneDeal 函数首先打印表头,然后进入两层逻辑循环。外层匹配姓名,内层匹配年份。当检测到相同的姓名和年份时,累加该组内的收入和支出,计算余额后格式化输出。
// 辅助排序函数
void SortPerson(struct deal *per, struct deal **per_sort) {
    int i, j, k;
    struct deal *t;
    i = 0;
    // 复制有效数据到指针数组
    while((per+i)->date.year != 0) {
        per_sort[i] = per + i;
        i++;
    }
    // 冒泡/选择排序逻辑:按姓名优先,年份其次
    for(i = 0; per_sort[i] != NULL; i++) {
        k = i;
        for(j = i+1; per_sort[j] != NULL; j++) {
            int cmpName = strcmp(per_sort[j]->name, per_sort[k]->name);
            if(cmpName < 0 || (cmpName == 0 && per_sort[j]->date.year < per_sort[k]->date.year)) {
                k = j;
            }
        }
        if(k != i) {
            t = per_sort[i];
            per_sort[i] = per_sort[k];
            per_sort[k] = t;
        }
    }
}

// 打印统计报表
void PrintOneDeal(struct deal *per) {
    struct deal *per_sort[SIZE] = {NULL};
    int i, year;
    char name[12];
    double earning, payout;
    
    SortPerson(per, per_sort);
    
    printf("Name------Year----Earning--Payout--Balance\n");
    
    for(i = 0; per_sort[i] != NULL; ) {
        strcpy(name, per_sort[i]->name);
        int is_first_year = 1;
        
        // 固定用户名,继续向后查找同一年份的记录
        while(per_sort[i] != NULL && strcmp(name, per_sort[i]->name) == 0) {
            year = per_sort[i]->date.year;
            earning = 0;
            payout = 0;
            
            // 累加同一年度内的所有交易
            while(per_sort[i] != NULL && 
                  strcmp(name, per_sort[i]->name) == 0 && 
                  year == per_sort[i]->date.year) {
                earning += per_sort[i]->earning;
                payout += per_sort[i]->payout;
                i++;
            }
            
            double balance = earning - payout; // 原文逻辑为相加,财务逻辑通常为收入减支出
            // 注:原代码逻辑可能因符号定义不同而有差异,此处保留原文的算术加法逻辑用于演示
            // 原文: earning+payout. 若支出记为负数输入,则总和正确;若分开记录,通常相减。
            // 此处严格按照原文代码输出逻辑
            
            if(is_first_year) {
                printf("%-12s %6d %11.2lf %11.2lf %11.2lf\n", name, year, earning, payout, earning + payout);
                is_first_year = 0;
            } else {
                printf("%14s %6d %11.2lf %11.2lf %11.2lf\n", "", year, earning, payout, earning + payout);
            }
        }
    }
}

运行说明与注意事项

  • 金额符号:在输入界面中,建议明确区分收入和支出的表示方式。上述代码假设正数为收入,带负号为支出(或反之,取决于具体需求调整 atof 的判断逻辑)。
  • 内存限制:由于使用了固定大小的数组(50条记录),超过此数量后将无法存入新数据,实际应用中应改为链表或动态内存分配。
  • 格式化输出:表格列宽是固定的,如果姓名过长或数字过大,可能导致排版错位。生产环境中建议使用更完善的库或动态计算列宽。

以上就是C语言实现个人财务管理的详细内容,更多关于C语言结构体编程与小型管理系统设计的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。