当前位置: 首页 > article >正文

从PTA L1-009出发:手把手教你用C语言写一个通用的‘分数计算器’

从PTA L1-009到工程实践构建可复用的C语言分数计算库在编程学习过程中我们常常会遇到这样的困境刷题时能够解决特定问题但当需要将类似功能应用到实际项目中时却不知如何下手。PTA的L1-009题目提供了一个很好的分数运算案例但它的实现往往局限于题目本身的输入输出格式。本文将带你从这道基础题目出发构建一个真正可复用、模块化的分数计算库让你的代码从解题专用升级为工程可用。1. 设计分数结构体与基础运算1.1 定义合理的分数结构在原始题目中我们直接使用两个long long变量存储分子和分母。但在实际工程中这种松散的数据组织方式不利于代码维护和功能扩展。我们应该定义一个专门的结构体来封装分数数据typedef struct { long long numerator; // 分子 long long denominator; // 分母 } Fraction;这种封装方式有三大优势类型安全明确区分分数与其他数值类型可扩展性未来可以方便地添加新字段如分数标志位接口清晰函数参数和返回值更加语义化1.2 实现核心运算函数基于结构体我们可以实现一组基础运算函数// 求最大公约数GCD long long gcd(long long a, long long b) { return b 0 ? a : gcd(b, a % b); } // 分数化简 void simplify_fraction(Fraction* frac) { if (frac-denominator 0) { frac-numerator * -1; frac-denominator * -1; } long long common_divisor gcd(llabs(frac-numerator), llabs(frac-denominator)); frac-numerator / common_divisor; frac-denominator / common_divisor; } // 分数加法 Fraction add_fractions(Fraction a, Fraction b) { Fraction result; result.numerator a.numerator * b.denominator b.numerator * a.denominator; result.denominator a.denominator * b.denominator; simplify_fraction(result); return result; }注意这里我们处理了分母为负的情况确保符号始终出现在分子上这是原始题目中容易忽略的细节。2. 构建完整的运算体系2.1 四则运算实现除了加法一个完整的分数计算库应该支持所有基本运算// 分数减法 Fraction subtract_fractions(Fraction a, Fraction b) { Fraction negative_b { -b.numerator, b.denominator }; return add_fractions(a, negative_b); } // 分数乘法 Fraction multiply_fractions(Fraction a, Fraction b) { Fraction result { a.numerator * b.numerator, a.denominator * b.denominator }; simplify_fraction(result); return result; } // 分数除法 Fraction divide_fractions(Fraction a, Fraction b) { if (b.numerator 0) { fprintf(stderr, Error: Division by zero\n); exit(EXIT_FAILURE); } Fraction reciprocal_b { b.denominator, b.numerator }; return multiply_fractions(a, reciprocal_b); }2.2 比较运算与工具函数为了提升库的实用性我们还需要添加比较功能和辅助函数// 比较两个分数是否相等 int fractions_equal(Fraction a, Fraction b) { simplify_fraction(a); simplify_fraction(b); return a.numerator b.numerator a.denominator b.denominator; } // 将字符串转换为分数 Fraction parse_fraction(const char* str) { Fraction result {0, 1}; sscanf(str, %lld/%lld, result.numerator, result.denominator); if (result.denominator 0) { fprintf(stderr, Error: Denominator cannot be zero\n); exit(EXIT_FAILURE); } simplify_fraction(result); return result; }3. 设计用户友好的交互界面3.1 命令行交互模式原始题目只处理特定格式的输入我们可以设计更灵活的命令行界面void interactive_mode() { printf(Fraction Calculator - Interactive Mode\n); printf(Enter expressions like 1/2 3/4 or quit to exit\n); char input[100]; while (1) { printf( ); fgets(input, sizeof(input), stdin); if (strncmp(input, quit, 4) 0) break; Fraction a, b; char op; if (sscanf(input, %lld/%lld %c %lld/%lld, a.numerator, a.denominator, op, b.numerator, b.denominator) 5) { Fraction result; switch (op) { case : result add_fractions(a, b); break; case -: result subtract_fractions(a, b); break; case *: result multiply_fractions(a, b); break; case /: result divide_fractions(a, b); break; default: printf(Unknown operator: %c\n, op); continue; } print_fraction(result); } else { printf(Invalid input format. Example: 1/2 3/4\n); } } }3.2 格式化输出函数良好的输出格式能大大提升用户体验void print_fraction(Fraction frac) { simplify_fraction(frac); if (frac.denominator 1) { printf(%lld\n, frac.numerator); } else if (llabs(frac.numerator) frac.denominator) { long long integer_part frac.numerator / frac.denominator; long long new_numerator llabs(frac.numerator) % frac.denominator; printf(%lld %lld/%lld\n, integer_part, new_numerator, frac.denominator); } else { printf(%lld/%lld\n, frac.numerator, frac.denominator); } }4. 错误处理与边界情况4.1 输入验证机制健壮的程序必须处理各种异常输入Fraction safe_parse_fraction(const char* str) { long long num, denom; int parsed sscanf(str, %lld/%lld, num, denom); if (parsed ! 2) { fprintf(stderr, Invalid fraction format: %s\n, str); exit(EXIT_FAILURE); } if (denom 0) { fprintf(stderr, Denominator cannot be zero\n); exit(EXIT_FAILURE); } Fraction result {num, denom}; simplify_fraction(result); return result; }4.2 内存与溢出保护处理大数运算时需要考虑溢出问题Fraction safe_add_fractions(Fraction a, Fraction b) { // 检查乘法是否会溢出 if (a.denominator LLONG_MAX / b.denominator) { fprintf(stderr, Error: Denominator multiplication overflow\n); exit(EXIT_FAILURE); } // 检查加法是否会溢出 long long term1 a.numerator * b.denominator; long long term2 b.numerator * a.denominator; if ((term1 0 term2 LLONG_MAX - term1) || (term1 0 term2 LLONG_MIN - term1)) { fprintf(stderr, Error: Numerator addition overflow\n); exit(EXIT_FAILURE); } return add_fractions(a, b); }5. 构建可复用库的进阶技巧5.1 头文件设计与模块化创建fraction.h头文件来声明公共接口#ifndef FRACTION_H #define FRACTION_H typedef struct { long long numerator; long long denominator; } Fraction; Fraction add_fractions(Fraction a, Fraction b); Fraction subtract_fractions(Fraction a, Fraction b); Fraction multiply_fractions(Fraction a, Fraction b); Fraction divide_fractions(Fraction a, Fraction b); Fraction parse_fraction(const char* str); void print_fraction(Fraction frac); #endif5.2 单元测试框架集成为分数库编写测试用例确保可靠性void test_fraction_operations() { Fraction a parse_fraction(1/2); Fraction b parse_fraction(3/4); // 测试加法 Fraction sum add_fractions(a, b); assert(fractions_equal(sum, parse_fraction(5/4))); // 测试减法 Fraction diff subtract_fractions(a, b); assert(fractions_equal(diff, parse_fraction(-1/4))); // 测试乘法 Fraction product multiply_fractions(a, b); assert(fractions_equal(product, parse_fraction(3/8))); // 测试除法 Fraction quotient divide_fractions(a, b); assert(fractions_equal(quotient, parse_fraction(2/3))); printf(All tests passed!\n); }5.3 性能优化考虑对于频繁调用的函数可以进行优化// 内联简化函数以避免函数调用开销 static inline void simplify_fraction_inline(Fraction* frac) { long long a llabs(frac-numerator); long long b llabs(frac-denominator); while (b ! 0) { long long temp b; b a % b; a temp; } frac-numerator / a; frac-denominator / a; if (frac-denominator 0) { frac-numerator * -1; frac-denominator * -1; } }在实际项目中这种从特定题目到通用工具的演进过程正是初学者成长为合格开发者的关键一步。当你再次面对编程题目时不妨多思考一步这个解决方案能否抽象为更通用的工具这种思维习惯将大大提升你的工程能力。

相关文章:

从PTA L1-009出发:手把手教你用C语言写一个通用的‘分数计算器’

从PTA L1-009到工程实践:构建可复用的C语言分数计算库 在编程学习过程中,我们常常会遇到这样的困境:刷题时能够解决特定问题,但当需要将类似功能应用到实际项目中时,却不知如何下手。PTA的L1-009题目提供了一个很好的分…...

如何快速配置多语言OCR:OCRmyPDF完整指南

如何快速配置多语言OCR:OCRmyPDF完整指南 【免费下载链接】OCRmyPDF OCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to be searched 项目地址: https://gitcode.com/GitHub_Trending/oc/OCRmyPDF 你是否曾遇到过扫描的PDF文件无法搜…...

3大核心优势解锁Windows本地实时语音转文字:TMSpeech深度解析

3大核心优势解锁Windows本地实时语音转文字:TMSpeech深度解析 【免费下载链接】TMSpeech 腾讯会议摸鱼工具 项目地址: https://gitcode.com/gh_mirrors/tm/TMSpeech 还在为会议记录手忙脚乱?担心语音数据上传云端泄露隐私?或者厌倦了网…...

10华夏之光永存:电磁弹射+一次性火箭航天入轨方案【第十篇:方案整体风险评估与国家落地实施建议】

华夏之光永存:电磁弹射一次性火箭航天入轨方案【第十篇:方案整体风险评估与国家落地实施建议】核心备注:本文为该系列第十篇风险评估与落地方案篇,系列共计10篇保姆式开源落地白皮书的最终篇,全文基于航天工程全生命周…...

LinkSwift:八大网盘直链下载助手终极指南 - 免费获取高速下载链接的完整教程

LinkSwift:八大网盘直链下载助手终极指南 - 免费获取高速下载链接的完整教程 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 …...

AI编程助手PUA技能:用绩效改进计划激发代码助手高能动性

1. 项目概述:当AI遇上“绩效改进计划”,一个让代码助手不再轻易放弃的底层技能如果你用过Claude Code、Cursor或者GitHub Copilot这类AI编程助手,大概率经历过这样的挫败感:你让它修复一个复杂的Bug,它尝试了两三次&am…...

从UTC到Asia/Shanghai:一份给Java开发者的服务器时间配置与代码兼容性指南

从UTC到Asia/Shanghai:Java开发者必知的时间处理实战手册 当你在本地开发环境测试通过的代码,部署到服务器后突然发现所有时间记录都差了8小时——这种"时区漂移"现象是Java开发者最常见的部署陷阱之一。本文将带你深入理解时区问题的本质&…...

别再硬写插件了!金蝶云单据下推转换规则的高级配置技巧(含子单据体过滤)

金蝶云单据下推转换规则深度解析:从基础配置到高阶过滤技巧 1. 理解单据下推的核心逻辑与业务场景 金蝶云星空系统的单据下推功能是企业业务流程自动化的重要枢纽。想象这样一个场景:销售订单需要转化为发货通知单时,传统操作需要人工重新录入…...

路径规划内存告急?手把手教你用RRT算法为嵌入式设备减负(附ROS实验对比)

路径规划内存告急?手把手教你用RRT算法为嵌入式设备减负(附ROS实验对比) 在资源受限的嵌入式机器人开发中,内存管理往往成为制约系统性能的瓶颈。当你的树莓派或Jetson Nano在运行A*算法时频繁触发内存告警,或是因地图…...

保姆级教程:在Abaqus中关闭S4R单元沙漏控制,让仿真结果更准(附Python脚本)

工程实战:Abaqus S4R单元沙漏控制优化与Python自动化解决方案 在结构仿真领域,Abaqus作为行业标杆软件,其S4R单元因其优秀的计算效率和适应性,成为板壳分析的首选。然而,许多工程师在实际应用中常遇到一个棘手问题&…...

避坑指南:在Linux下玩转NVIDIA GPU Direct时,那些关于IOMMU和地址映射的‘坑’与最佳实践

避坑指南:Linux下NVIDIA GPU Direct的IOMMU与地址映射实战解析 当你在凌晨三点盯着屏幕上那条令人窒息的NVRM: GPU at PCI:0000:3B:00.0: GPU does not support NvLink报错时,就会明白——高性能计算从来不是简单的apt-get install就能解决的。本文将带你…...

保姆级教程:在Vue3项目中从零配置AntV X6图编辑引擎(含对齐线插件)

Vue3项目深度整合AntV X6图编辑引擎实战指南 在当今数据驱动的应用开发中,可视化图编辑功能已成为企业级前端项目的标配需求。AntV X6作为阿里经济体内部孵化的专业级图编辑引擎,凭借其丰富的拓扑图、流程图定制能力和完善的插件生态,正在逐步…...

DroidCam OBS插件:三步将手机摄像头变为专业直播视频源

DroidCam OBS插件:三步将手机摄像头变为专业直播视频源 【免费下载链接】droidcam-obs-plugin DroidCam OBS Source 项目地址: https://gitcode.com/gh_mirrors/dr/droidcam-obs-plugin 将智能手机摄像头快速转化为高质量视频输入源,是内容创作者…...

别再只用bytes了!Python bytearray() 实战:5分钟搞定可变字节数据的读写与修改

别再只用bytes了!Python bytearray() 实战:5分钟搞定可变字节数据的读写与修改 当你第一次在Python中处理二进制数据时,bytes类型可能是你的首选。但当你需要频繁修改这些数据时,很快就会发现bytes的不可变性带来的不便。这时&…...

Electron 安全策略升级后,你的 Vue3 应用 IPC 通信该怎么写?一份避坑指南

Electron 安全策略升级后,你的 Vue3 应用 IPC 通信该怎么写?一份避坑指南 在桌面应用开发领域,Electron 凭借其跨平台能力和 Web 技术的易用性,已经成为构建现代桌面应用的首选框架之一。然而,随着 Electron 安全策略的…...

如何用YuukiPS启动器快速管理多账号动漫游戏:5个实用技巧

如何用YuukiPS启动器快速管理多账号动漫游戏:5个实用技巧 【免费下载链接】Launcher-PC 项目地址: https://gitcode.com/gh_mirrors/la/Launcher-PC 如果你经常在《原神》等动漫游戏中切换多个账号,或者为游戏补丁更新、网络连接问题烦恼&#x…...

格鲁吉亚语ASR系统开发:低资源语音识别实战

1. 项目概述:构建格鲁吉亚语自动语音识别系统作为一名长期从事语音识别技术研发的工程师,我最近完成了一个颇具挑战性的项目——为格鲁吉亚语开发高性能的自动语音识别(ASR)系统。格鲁吉亚语作为典型的小语种,其语音数据资源极为有限&#xf…...

AI代理循环Ralph:自动化代码生成与质量检查的工程实践

1. 项目概述:一个能“自己写代码”的AI代理循环如果你和我一样,对AI辅助编程工具(比如Amp或者Claude Code)又爱又恨,爱的是它们能快速生成代码片段,恨的是它们经常“健忘”,上下文一长就逻辑混乱…...

避开这些坑,你的数学建模论文能多拿10分:评委视角下的常见误区与排版实战

避开这些坑,你的数学建模论文能多拿10分:评委视角下的常见误区与排版实战 距离数学建模竞赛提交截止只剩24小时,你的团队是否还在为论文的最后一公里焦虑?作为曾参与多次竞赛评审的过来人,我发现90%的参赛队伍在模型求…...

从IDEA转VSCode做工业自动化开发?这7个调试断点失效原因,工程师凌晨三点还在查!

更多请点击: https://intelliparadigm.com 第一章:VSCode工业自动化调试的底层逻辑与认知重构 VSCode 并非传统意义上的 IDE,而是一个基于事件驱动、插件化架构的可扩展开发平台。在工业自动化场景中(如 PLC 逻辑仿真、OPC UA 协…...

泰勒级数:从数学理论到工程优化的实践指南

1. 泰勒级数:从数学基石到工程实践作为一名长期从事算法开发的工程师,我最初接触泰勒级数是在研究函数优化问题时。当时为了理解牛顿法的底层逻辑,不得不重新审视这个看似基础却无比强大的数学工具。泰勒级数展开不仅是数学分析中的核心概念&…...

霍格沃茨之遗稳定运行不崩溃设置:基于引擎优化与硬件排查的终极方案

作为一款对硬件资源“贪得无厌”的开放世界游戏,《霍格沃茨之遗》在带来顶级视觉享受的同时,也因其稳定性问题让不少PC玩家头疼。闪退、卡死、报错……这些问题时常打断我们的魔法冒险。但“稳定运行不崩溃”并非遥不可及,它需要对症下药的设…...

Qt Quick布局避坑指南:为什么我的RowLayout子项不显示?5个常见锚点冲突案例解析

Qt Quick布局避坑指南:为什么我的RowLayout子项不显示?5个常见锚点冲突案例解析 当你在Qt Quick项目中精心设计了RowLayout布局,却发现某些子项神秘消失时,那种挫败感就像在黑暗房间里寻找不存在的电灯开关。本文将带你深入五个典…...

除了管理用户,域服务器还能干啥?用Windows Server 2022的AD DS为FortiGate防火墙做流量认证

Windows Server 2022域服务与FortiGate防火墙的深度整合实践 在数字化转型浪潮下,企业网络架构正从传统的边界防御向零信任安全模型演进。作为身份管理核心的Active Directory域服务(AD DS)与下一代防火墙的联动,成为构建动态访问…...

LVGL 8.3在RT-Thread上的移植踩坑实录:从模拟器到真机显示的完整流程

LVGL 8.3在RT-Thread上的移植踩坑实录:从模拟器到真机显示的完整流程 在嵌入式开发领域,图形用户界面(GUI)的实现一直是开发者面临的挑战之一。LVGL作为一款轻量级、多功能的图形库,凭借其开源特性和丰富的功能组件,正成为越来越多…...

保姆级教程:在Spring Boot 2.x + Spring Cloud中正确配置OAuth2 Client的Secret(避坑BCrypt)

Spring Boot 2.x与Spring Cloud OAuth2客户端安全配置实战指南 在微服务架构中,OAuth2已经成为事实上的安全标准协议。但很多开发者在Spring Boot 2.x与Spring Cloud的版本组合中配置OAuth2客户端时,常常会遇到invalid_client错误。这通常是由于对Spring…...

线上热修复不求人:手把手教你用Arthas的jad、mc、redefine三件套无感更新Bug代码

线上热修复实战:用Arthas三件套实现无感代码更新 当生产环境突然爆出紧急Bug时,每个开发者都面临两难选择:要么顶着压力重启服务,要么忍受故障持续影响业务。去年双十一大促期间,我们的支付系统就遭遇过这样的惊魂时刻…...

三步完成Windows和Office永久激活:KMS_VL_ALL_AIO完整使用教程

三步完成Windows和Office永久激活:KMS_VL_ALL_AIO完整使用教程 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 还在为Windows系统频繁弹出激活提示而烦恼吗?Office文档突…...

别再乱用@Autowired注入HttpServletRequest了!SpringBoot请求对象获取的3个实战避坑点

SpringBoot中HttpServletRequest的三大高阶用法与避坑指南 在SpringBoot项目中,HttpServletRequest作为HTTP请求的入口对象,承载着参数解析、会话管理、请求分发等核心功能。许多开发者虽然能够通过各种方式获取Request对象,但对背后的运行机…...

Onekey:3分钟搞定Steam游戏清单的终极自动化方案

Onekey:3分钟搞定Steam游戏清单的终极自动化方案 【免费下载链接】Onekey Onekey Steam Depot Manifest Downloader 项目地址: https://gitcode.com/gh_mirrors/one/Onekey 你是否曾为获取Steam游戏清单而头疼?传统的繁琐流程需要手动调用API、处…...