当前位置:首页>java>韩江夜话第五盏:C语言之模块化编程·函数设计

韩江夜话第五盏:C语言之模块化编程·函数设计

  • 2026-02-05 00:44:35
韩江夜话第五盏:C语言之模块化编程·函数设计

音频速读

时值寒冬,韩江静淌,广济桥默然横波。春哥茶馆二楼,一盏暖灯已亮了四夜。

窗棂外的月光格外清亮,映着江心一点渔火,也映着茶台上三副凝神的面孔——阿明、阿雅、小蔡。桌上摊开的不是茶谱,而是一张画得密密麻麻、如同“经脉运行图”的腊纸,纸上写着《C程序设计》第7章的标题,旁边是用红蓝记号笔圈了又圈的函数图。

“前四盏,”春哥提起红泥小炉上已滚如“蟹目”的沸水,缓缓注入一只紫砂胎的潮州手拉朱泥壶,“咱们一道砌了四块‘基石’。”

他指尖虚点,像在复盘一盘棋:

“第一盏,咱们立了总纲:‘程序 算法 数据结构’,说的是‘魂魄’与‘皮肉’。第二盏,咱们捏了‘皮肉’的基础——数据怎么表示、怎么捣鼓。第三盏,咱们通了‘经脉’——程序怎么流转。第四盏,咱们学了组织‘大军’——数组怎么摆布批量数据。”

夜风悄悄溜进来,腊纸的一角微微掀动,露出底下“第7章 用函数实现模块化程序设计”一行正楷标题。

“那么今夜,这第五盏,”春哥将冲出的第一道茶汤,金黄油亮,逐一倾入面前三只若深杯,“咱们要讲的,是这门C语言手艺里的 ‘开宗立派’、‘分治合击’ 之法。它叫 函数,也叫 模块化编程。它教你如何把一座庞大复杂的程序‘紫禁城’,拆解成一座座精巧独立、各有神通、又能严丝合缝拼回去的‘潮汕四点金’与‘下山虎’。”

第一回:破题——“把大象装冰箱”与函数的由来:为什么要有“门派”?

春哥没急着翻书,而是摸出手机,投影出一段极简的代码。那是第一盏露过面的“加法计算器”升级版,代码却长到几乎占满屏幕,上下翻滚。

#include<stdio.h>intmain(){    // ……    // 整整两百行代码,包含:用户菜单、输入验证、加减乘除运算、错误处理、结果格式化、历史记录……    // 所有逻辑,全挤在一个 main() 函数的大括号 {} 里。    // 读起来,像一碗没放盐、没下料的“白粥拌一切”。}

“这代码,能跑吗?或许能。”春哥问,“好读吗?好改吗?好让别人接你的手吗?”

三人齐齐摇头。

“这就叫 ‘面条代码’ 的晚期症状。”春哥点开教材第167页,引出谭浩强先生的发问:“为什么要用函数?”他高声念出书里的答案:

“在设计一个较大的程序时,往往把它分为若干个程序模块,每一个模块包括一个或多个函数,每个函数实现一个特定的功能。一个C程序可由一个主函数和若干个其他函数构成。……函数间的调用关系,如同搭积木,更容易实现大型程序的开发。”

“简单说,”春哥将复杂的加法计算器需求,用笔尖在腊纸上“庖丁解牛”,“一个完整的计算器,至少要干这几件事:

(1显示菜单,让用户选(这是个独立功能)

(2读取用户输入的数字(这又是个独立功能,且可能被反复调用)

(3做加法(核心算法)

(4做减法(另一个核心算法)

(5处理输入错误(比如用户输入了字母)

(6漂亮地打印结果(格式化输出)

“如果所有代码都堆在 main() 里,main() 就成了一个‘啥都管的掌柜’,累死不说,店里一旦起火(出bug),你连火源在哪个旮旯都找不到。而 函数(模块化)的思想,就是给这个‘全能掌柜’招兵买马,分封诸侯:

- show_menu() 函数,就管“挂招牌、唱菜名”。

- get_number_from_user() 函数,就管“接客、记单”。

- add_two_numbers() 函数,就管“后厨炒青菜”。

- subtract_two_numbers() 函数,就管“后厨炖老汤”。

- handle_input_error() 函数,就管“处理客诉”。

- print_result() 函数,就管“端菜上桌,唱个肥喏”。

“每个‘伙计’(函数)分工明确,手艺专精,只通过清晰的‘接口’(输入参数和返回值)与外界打交道。main() 这个‘大掌柜’,只需在恰当的时机,喊一嗓子:‘张三,去炒个菜!李四,去接个客!’——程序的天下,由此而治。”

第二回:立派——“开山门,定规矩”:函数的定义、声明与调用三部曲

春哥将投影切到教材第170页“怎样定义函数”一节。

“要立一个门派(函数),得有三样‘镇派之宝’:1. 门派章程(函数定义)、2. 江湖通告(函数声明)、3. 英雄帖(函数调用)。 咱们一样样来。”

第一宝:门派章程——函数的定义

“定义函数,就是在创造这个‘伙计’。”春哥在白板上写下标准格式:

返回类型 函数名(参数类型1 参数名1, 参数类型2 参数名2, …) {    // 函数体:这个“伙计”要干的活儿(语句序列)    return 返回值; // 可选,但若返回类型不是void,则必须有}

他随手拈来“潮汕牛肉丸店”的案例,定义一个“捶打牛肉丸”的函数:

// 函数定义:捶打牛肉丸// 返回类型:int(最终打出的丸子个数)// 函数名:make_beef_balls(见名知义)// 参数:float beef_weight(牛肉重量,公斤), int strength(捶打力度等级)intmake_beef_balls(float beef_weight, int strength){    printf("开始捶打 %.2f 公斤牛肉,力度等级 %d……\n", beef_weight, strength);    int beating_time = beef_weight * 60// 每公斤捶60下    for (int i = 1; i <= beating_time; i++) {        printf("咚!"); // 模拟捶打声        if (i % 20 == 0printf("\n"); // 每20下换行。实际系统要歇一歇,擦擦汗    }    printf("\n捶打完毕!\n");    int ball_count = beef_weight * 15// 假设每公斤出15颗丸子    return ball_count; // 返回制作出的丸子数量}

“看,”春哥逐行拆解,“int make_beef_balls(float beef_weight, int strength) 这一行,叫 函数首部(函数原型)。它定了三条‘门规’:

(1本门产出(返回值类型):int,代表做完丸子后,要“上报”一个整数(丸子个数)。

(2本门名号(函数名):make_beef_balls,名字就要让人一眼看懂它是干啥的。

(3拜入门下需备何礼(形参列表):(float beef_weight, int strength),想请我“捶丸子”,你得告诉我用多少牛肉(浮点数)、使多大力气(整数)。

“后面大括号 {} 里的,就是函数体,是这门手艺的‘独门心法’。最后return ball_count;,是‘交作业’,把结果(丸子数)报上去。”

第二宝:江湖通告——函数的声明 原型

“你定义了一个‘捶丸子’门派在城东,”春哥话锋一转,“但城主(main函数)在城中心办公,他怎么知道有你这一号人物,又怎么知道请你要备什么礼、你会回什么礼?这就需要提前发个‘江湖通告’(函数声明)。”

他翻到教材第176页“对被调用函数的声明和函数原型”一节。

“声明,就是函数的‘名片’,只包含函数首部(原型),后面加个分号,不包含函数体。它告诉编译器:‘喂,老兄,后面有个叫make_beef_balls 的家伙,你得认得他,调用他的规矩是这样的……’通常,我们把这‘名片’放在程序文件的开头,#include 之后。”

// 函数声明(江湖通告)intmake_beef_balls(float beef_weight, int strength)// 注意分号!

“为什么需要这个?因为C编译器是‘死脑筋’,它从上到下读代码。如果main 函数在第10行想调用make_beef_balls,但make_beef_balls 的定义在第50行,编译器读到第10行时就懵了:‘这make_beef_balls 是啥?我没见过!’于是报错。提前声明,就是给它‘备案’。”

第三宝:英雄帖——函数的调用

“城主(main函数)知道有你这么个门派了,真要找你干活时,就得下 ‘英雄帖’(函数调用)。”春哥在main 函数里写下:

intmain() {    // … 其他代码    // 函数调用:下英雄帖,请“捶丸子”门派出手    // 给出实际的“礼物”(实参):5.0公斤牛肉,力度等级3    int total_balls = make_beef_balls(5.03);    printf("今日共制作牛肉丸:%d 颗。\n", total_balls);    // … 其他代码    return 0;}

“调用时,写的make_beef_balls(5.0, 3),其中5.03是实际参数(实参)。它们会被‘传递’给函数定义里的形式参数(形参) beef_weight 和 strength。”春哥画出示意图:

调用:make_beef_balls(5.03)        |        |        |        |定义:int make_beef_balls(float beef_weight, int strength)                                   ↑                  ↑                        实参5.0传给beef_weight  实参3传给strength

“整个流程,”春哥总结,“就是:先声明(广而告之),再定义(立下章程),后在需要处调用(下达指令)。 这也是谭浩强老师在书中强调的调用过程:‘当调用一个函数时,实参的值传递给形参,流程控制转移到被调用函数,执行其函数体,完成后带着返回值(如有)回到调用处。’”

第三回:传功——“值传”与“址传”:函数间的“飞鸽传书”与“地图指路”

阿雅举手:“春哥,如果我在main 里有个变量my_beef = 10.5,我调用make_beef_balls(my_beef, 3),函数里改了beef_weightmain里的my_beef 会变吗?”

“好问题!触及了C语言函数传参的核心机密:传值调用。”春哥切到教材第172页“函数调用时的数据传递”。

传值调用——飞鸽传书,只传抄本

“在C语言里,默认情况下,函数的参数传递是‘传值’。”春哥打了个比方,“好比你有本祖传菜谱(变量my_beef)。你想请我(函数)帮你改良一下。‘传值’的方式是:你不是把原本给我,而是自己工工整整抄一份副本(创建一个新的临时变量),把副本交给我。我在副本上写写画画(修改形参),改得天花乱坠,但你的原本菜谱,安然无恙。”

他写代码验证:

voidtry_to_change(int copy){    copy = 999// 改的是副本    printf("函数内,copy = %d\n", copy);}intmain(){    int original = 100;    printf("调用前,original = %d\n", original);    try_to_change(original); // 传递的是 original 的“值副本”    printf("调用后,original = %d\n", original); // 还是100!    return 0;}

运行结果:

调用前,original = 100函数内,copy = 999调用后,original = 100

“看到了吗?函数内部翻天覆地,外部世界(original)纹丝不动。这就是‘传值’。对于基本数据类型(int, float, char 等),C语言默认采用传值。 好处是安全,函数不会意外篡改外部数据。坏处是,如果我想让函数真正修改外部变量,这招就不灵了。”

传址调用(借助指针)——地图指路,直捣黄龙

“那如何才能让函数修改外部变量呢?”春哥眼神一亮,“这就需要请出咱们第三盏的老朋友,也是其中的主角——指针。这里先浅尝辄止,理解其思想。”

“指针,犹如一张藏宝图(内存地址)。传址调用,就是我不给你牛肉(值),而是给你一张写着‘牛肉藏于我家后院第三棵树下’的纸条(地址)。你拿着这张纸条,可以直奔目的地,把树下的牛肉取走或换掉。”

// 参数是一个 int 类型的指针,用于接收地址voidreally_change(int *map_to_value){    *map_to_value = 999// 通过指针(地图),找到真实位置并修改    printf("函数内,通过地图找到了值:%d\n", *map_to_value);}intmain(){    int treasure = 100;    printf("调用前,treasure = %d\n", treasure);    really_change(&treasure); // 传递的是 treasure 的“地址”(& 是取地址符)    printf("调用后,treasure = %d\n", treasure); // 变成了999!    return 0;}

“注意,”春哥强调,“really_change(&treasure)中的是‘取地址运算符’,它把变量treasure 的‘家门牌号’(内存地址)拿出来。函数形参int *map_to_value 中的表示map_to_value 是一个 指针变量,专门用来存放‘家门牌号’(地址)。函数体内*map_to_value = 999; 中的*是解引用运算符,意思是:‘沿着这张地图(map_to_value) 所指的地址找过去,对那里的东西进行赋值。’”

“这就是传址调用的模拟(C语言没有真正的‘引用’,但用指针可以实现同样效果)。当需要函数修改外部变量,或者传递大型数据(如数组,传整个数组太慢,传其地址则高效)时,就必须用‘传址’。数组名作为函数参数时,传递的就是数组首元素的地址。”

第四回:布阵——“嵌套”与“递归”:门派里的“套娃”与“镜子迷宫”

函数嵌套调用——江湖套娃,层层递进

“一个门派(函数)里,可以请另一个门派(函数)来帮忙吗?当然可以,这叫函数的嵌套调用。”春哥翻到教材第179页。

“好比‘潮汕宴席筹备总部’(main) 要办酒,它把事情分给‘冷盘部’ (prepare_cold_dishes())。‘冷盘部’自己忙不过来,又去请‘卤味专营店’(make_braised_food()) 和‘生腌小铺’(make_raw_marinated()) 来供货。这就是嵌套。”

voidmake_braised_food()printf("  正在卤制狮头鹅……\n"); }voidmake_raw_marinated()printf("  正在腌制血蚶……\n"); }voidprepare_cold_dishes(){    printf("冷盘部开工:\n");    make_braised_food();  // 调用另一个函数    make_raw_marinated(); // 调用又一个函数    printf("冷盘备齐!\n");}intmain(){    printf("宴席筹备开始:\n");    prepare_cold_dishes(); // 调用函数,其内部又调用了其他函数    printf("宴席筹备完成。\n");    return 0;}

“调用流程就像栈一样,一层层进去,再一层层回来。编译器会自动管理这个‘调用栈’。这是实现复杂功能分解的必然。”

函数递归调用—照见自己,无限镜像

“如果,一个门派(函数),自己请自己帮忙呢?”春哥抛出更深刻的问题,指向教材第181页“函数的递归调用”。

“这听起来像悖论,但在数学和编程中,这叫递归。一个直接或间接调用自身的函数,就是递归函数。”

经典案例:计算阶乘(n!) 

n! = n * (n-1)!,且 0! = 1。定义里就用到了自己。用C语言递归实现:”

// 递归函数:计算阶乘longlongfactorial(int n) {    if (n == 0 || n == 1) { // 递归基:最简单的情况,直接有答案        return 1;    } else {        return n * factorial(n - 1); // 递归步:问题规模缩小,调用自身    }}intmain() {    int num = 5;    printf("%d! = %lld\n", num, factorial(num));    return 0;}

“我们走查一下factorial(5)的计算过程:

1. factorial(5)5 != 01,进入else,需要计算5 * factorial(4)。但 factorial(4) 还不知道,调用暂停,先算 factorial(4)

2. factorial(4):需要 4 * factorial(3),又暂停,去算 factorial(3)

3. factorial(3):需要 3 * factorial(2),暂停,算 factorial(2)

4. factorial(2):需要 2 * factorial(1),暂停,算 factorial(1)

5. factorial(1):满足 n==1,递归基,直接返回 1

6. 回到 factorial(2):拿到 factorial(1)=1,计算 2 * 1 = 2,返回 2

7. 回到 factorial(3):拿到 2,计算 3 * 2 = 6,返回 6

8. 回到 factorial(4):拿到 6,计算 4 * 6 = 24,返回 24

9. 回到 factorial(5):拿到 24,最终计算 5 * 24 = 120,返回。

“这个过程,”春哥画出一个层层堆叠又层层消解的结构,“好似一面镜子对着另一面镜子,影像无限嵌套下去(递推),直到遇到一个终点(递归基),然后所有的影像又依次反射回来(回归)。

递归的精髓在于两点:

(1)找到递归基(最简单、不可再分的情况);

(2)确保每次递归调用,问题规模都在向递归基缩小。

“递归思维极美,能优雅解决许多问题(如汉诺塔、斐波那契数列、目录树遍历)。但要注意,递归调用消耗栈空间,深度过大会导致‘栈溢出’。对于能清晰转化为循环的问题,有时用循环(迭代)效率更高。”

第五回:辖地——变量的“人生”与“地盘”:局部与全局,静态与动态

“函数立了门派,变量作为‘资源’(数据),就得划分‘地盘’(作用域)和‘寿命’(生命周期)。否则会天下大乱。”春哥进入教材第196-209页的核心区。

局部变量——门派私产,生于斯,死于斯

在函数内部定义的变量,就是局部变量。”春哥指向前面的make_beef_balls 函数,“比如里面的beating_time, i, ball_count。它们的特点:

作用域:只在定义它的函数内部(或更内的代码块)可见。出了这个函数,没人认识它。main 函数想直接用 ball_count?没门!

生命周期:函数被调用时诞生,函数执行完毕时消亡。每次调用,都是崭新的变量。

存储位置:通常存储在栈区。

“这保证了函数的封装性和独立性。不同函数里完全可以有同名的局部变量,它们互不干扰,就像不同门派里都有个叫‘张三’的伙计。”

全局变量——江湖公产,无处不在,长生不老

在所有函数之外(通常是在程序文件开头)定义的变量,就是 全局变量。”

#include <stdio.h>int global_tea_price = 68// 全局变量,定义在函数外voidprint_price() {    printf("今日茶价:%d 文\n", global_tea_price); // 任何函数内都可访问}voidchange_price() {    global_tea_price = 75// 任何函数内都可修改}intmain() {    print_price();    change_price();    print_price(); // 价格已被改变    return 0;}

“全局变量的特点:

作用域:从定义处开始,到整个程序文件结束。所有后续函数都能看到并修改它。

生命周期:程序启动时诞生,程序结束时消亡。与程序同寿。

存储位置:存储在 静态存储区 或 数据段。

慎用全局变量!”春哥的语气陡然加重,“谭浩强老师在书中提醒:‘除非十分必要,不要使用全局变量,因为:①全局变量在程序全部执行过程中都占用存储单元;②降低了函数的通用性…;③降低了程序的清晰性…’。

“滥用全局变量,会让函数间的依赖关系变得隐晦、混乱,代码难以理解和维护。它破坏了模块化的纯洁性。应优先考虑通过函数参数和返回值来传递数据。”

静态局部变量——深藏不露,记忆犹新

“有没有一种变量,既想保持局部性(只在函数内可见)拥有‘记忆’(函数调用结束后值不丢失)?有,这就是静态局部变量,static 关键字修饰。”

voidcounter() {    static int call_count = 0// 静态局部变量,只初始化一次    call_count++;    printf("本函数已被调用了 %d 次。\n", call_count);}intmain() {    counter(); // 输出:1    counter(); // 输出:2    counter(); // 输出:3    return 0;}

static int call_count = 0; 这句,初始化只在程序第一次执行到此时进行一次。以后每次调用 counter()call_count 都会保留上一次的值。它的生命周期与全局变量相同(程序始终存在),但作用域仍局限于函数内。它存储在静态存储区。”

变量存储类别小结(内存中的‘居所’)

春哥画出一张简图:

“理解变量住在哪里、活多久、谁能看见它,是写出健壮、高效程序的基础。这也是模块化设计中 信息隐藏 和 数据封装 原则的体现。”

第六回:合纵——“内部”与“外部”:门派的内外之功

“当你的程序越来越大,需要分开写在多个源文件(.c 文件)中时,函数的‘可见范围’又有了新的规矩:内部函数与外部函数。”春哥讲到教材第212页。

内部函数(静态函数)——家传绝学,秘不示人

只允许在本源文件内调用的函数,叫内部函数,也叫静态函数。定义时在函数类型前加static 关键字。”

// file1.cstatic void secret_recipe() { // static 关键字    printf("这是祖传牛肉丸秘方,不外传!\n");}void public_service() {    secret_recipe(); // 在同一个文件里可以调用}

“在另一个文件file2.c中,无法调用secret_recipe()。这就保护了核心实现细节,实现了模块的强内聚。”

外部函数(默认)——广开山门,来者不拒

C语言默认的函数就是外部函数。可以被其他源文件调用。要在其他文件中调用,通常需要在调用前用extern 关键字进行外部声明。”

// file1.cvoidpublic_service() { // 默认就是外部函数    printf("提供公开的捶丸服务。\n");}// file2.c#include <stdio.h>externvoidpublic_service()// 外部声明,告诉编译器此函数在其他文件intmain() {    public_service(); // 正确调用 file1.c 中的函数    return 0;}

“在大型项目中,通常将函数的声明集中写在.h头文件中,然后在需要的.c 文件中#include 这个头文件。.h 文件就像公开的‘武功目录’,.c 文件是具体的‘内功心法’。这是模块化开发的基石。”

第七回:实战——重构“猜数字”与“茶铺账房”的模块化江湖

理论已备,茶汤已凉至适口。春哥带领三人,开启今夜的终极实战——用模块化思维,重构第三盏的“猜数字游戏”和第二盏的“茶铺账房系统”。

项目一:模块化重构“猜数字游戏”

“原来的游戏代码全在main() 里。现在,我们把它拆分成几个清晰的模块。”

第一步:自顶向下设计模块

1. 游戏控制模块(main):总指挥。

2. 游戏初始化模块(init_game):生成随机数、初始化尝试次数。

3. 单轮猜测处理模块(process_guess):处理用户输入一次猜测,并反馈大小。

4. 游戏结果显示模块(show_game_result):显示胜利或失败信息。

第二步:编写模块化代码

#include<stdio.h>#include<stdlib.h>#include<time.h>// ========== 函数声明(江湖名录) ==========voidinit_game(int *secret, int *attempts_left);intprocess_guess(int secret, int guess);voidshow_game_result(int victory, int secret, int attempts_used);// ========== 函数定义(门派章程) ==========// 模块1: 初始化游戏voidinit_game(int *secret, int *attempts_left){    srand(time(NULL));    *secret = rand() % 100 + 1// 通过指针修改外部变量    *attempts_left = 7;    printf("游戏开始!我已想好一个1-100之间的数字。\n");}// 模块2: 处理一次猜测// 返回:1表示猜中,0表示未猜中intprocess_guess(int secret, int guess){    if (guess == secret) {        return 1// 猜中    } else if (guess < secret) {        printf("不对,我想的数字比 %d **大**。\n", guess);    } else {        printf("不对,我想的数字比 %d **小**。\n", guess);    }    return 0// 未猜中}// 模块3: 显示游戏最终结果voidshow_game_result(int victory, int secret, int attempts_used){    printf("\n================================\n");    if (victory) {        printf("恭喜!你用了%d次猜中了数字%d!\n", attempts_used, secret);    } else {        printf("很遗憾,机会用尽。正确答案是:%d\n", secret);    }    printf("================================\n");}// ========== 主控模块 ==========intmain(){    int secret_number, attempts_left, current_guess;    int guess_correct = 0// 标志是否猜中    // 1. 初始化    init_game(&secret_number, &attempts_left);    // 2. 游戏主循环    while (attempts_left > 0 && !guess_correct) {        printf("你还剩%d次机会,请输入你的猜测:", attempts_left);        if (scanf("%d", &current_guess) != 1) { // 简单的输入验证            printf("输入无效,请重新输入数字。\n");            while(getchar() != '\n'); // 清空输入缓冲区            continue;        }        attempts_left--;        guess_correct = process_guess(secret_number, current_guess);    }    // 3. 显示结果    show_game_result(guess_correct, secret_number, 7 - attempts_left);    return 0;}

“看,”春哥指着代码,“main()现在多清爽!它只负责流程调度。具体的脏活累活(生成随机数、判断大小、显示结果)都交给了各个专业模块。这代码易读、易改、易测。如果想增加难度(比如改变数字范围或尝试次数),只需修改init_game 模块;如果想改变提示语,只需修改process_guess 模块。”(春哥注:程序架构的优劣,读一读 main() 函数便知。设计精良的程序,其主函数逻辑应当如一份清晰的解决方案提纲,自上而下,一目了然

项目二:模块化设计“春哥茶铺智能账房系统”

“这是一个更综合的案例,融合了前四盏所有知识,并用今夜所学的模块化进行顶层设计。”

需求分析(自顶向下分解):

1. 核心数据(数据结构):

茶品信息(名称、单价、库存)

订单信息(茶品、数量、折扣、总额)

会员信息(编号、姓名、积分)

2. 核心功能模块(算法/函数):

- manage_menu():显示主菜单(点单、结账、库存查询、会员管理)。

- place_order():处理点单流程。

- calculate_bill():计算订单总额(含折扣、会员价)。

- update_inventory():更新库存。

- handle_membership():处理会员积分。

- generate_receipt():生成并打印收据。

代码框架展示(因篇幅,展示核心结构):

#include<stdio.h>#include<string.h>#define MAX_TEA 10#define MAX_NAME_LEN 20// ========== 数据结构定义 ==========typedef struct {    int id;    char name[MAX_NAME_LEN];    float price;    int stock;} TeaItem;typedef struct {    int tea_id;    int quantity;} OrderItem;// ========== 全局数据(谨慎使用) ==========TeaItem tea_menu[MAX_TEA];int tea_count = 0;// ========== 函数声明 ==========voidinit_menu();voidshow_main_menu();voidplace_order();floatcalculate_bill(OrderItem order[], int item_count, int is_member);voidprint_receipt(OrderItem order[], int item_count, float total);// ========== main函数 ==========intmain(){    init_menu(); // 初始化茶单    int choice;    do {        show_main_menu();        scanf("%d", &choice);        switch(choice) {            case 1place_order(); break;            case 2printf("结账功能待实现。\n"); break;            case 3printf("库存查询待实现。\n"); break;            case 4printf("会员管理待实现。\n"); break;            case 0printf("感谢使用,再见!\n"); break;            defaultprintf("无效选择!\n");        }    } while(choice != 0);    return 0;}// 此处是各个函数的详细实现……

“这个框架,”春哥说,“已经具备了可扩展的骨架。每个case 后面调用的函数,都可以由你们在未来几天,作为‘课后修炼’,一个一个去实现和完善。这便是 模块化开发的真正威力:分工协作,增量迭代。”

第八回:心法——“高内聚、低耦合”与“单一职责”:模块化设计的至高境界

茶已过五泡,窗外夜色如墨,韩江的水声却愈发清晰起来,像是从地底传来的脉搏。春哥将最后一泡茶汤均匀分入杯中,动作慢了下来,语速却更沉、更深。

“前面七回,咱们练的是‘招’。”他放下茶壶,目光扫过三人,“怎么定义函数、怎么传参、怎么调用、怎么划分变量地盘——这些都是‘术’,是你在程序江湖里安身立命的‘一招鲜’。”

“接下来这一回,”他端起茶杯,却不急着喝,“咱们要聊的是‘心法’。是那些看不见、摸不着,却决定了你写的代码是‘能用就好’,还是‘经得起风雨’的根本法则。”

阿明往前倾了倾身子:“就像武侠小说里的内功?”

“比那还深。”春哥点头,“内功练的是气,心法修的是道。在模块化编程里,这道,可以凝练成六个字——”

他在茶盘上,用茶针缓缓划出两行字:

高内聚

低耦合

一、高内聚:一个萝卜一个坑,一颗茶种一座山

“先说‘高内聚’。”春哥从茶柜里取出三个不同的茶罐,一字排开,“你们看这凤凰单丛、蜜兰香、鸭屎香——每罐只装一种茶,绝不混杂。这叫‘专一’。”

“高内聚,说的就是:一个函数,应该只做好一件事,并把这件事做透、做完整。”

他翻开教材,找到自己夹着书签的那一页:“谭浩强老师在书里没直接写这四个字,但字里行间都是这个意思。你看他举的例子——‘求两个数的最大值’、‘判断素数’、‘排序’——每个函数目标清晰,功能单一。”

反面教材:什么都是,什么都不精

春哥打开投影,放出一段“茶铺账房系统”的早期代码:

// 糟糕的例子:一个函数干了五件事voidhandle_order_and_account(){    // 1. 显示菜单    printf("1. 点单 2. 结账 3. 查询库存\n");    scanf("%d", &choice);    // 2. 处理点单    if (choice == 1) {        // 点单逻辑100行……    }    // 3. 计算账单    float total = 0;    // 计算逻辑80行……    // 4. 更新库存    // 更新逻辑50行……    // 5. 打印收据    // 打印逻辑60行……    // 6. 记录日志(突然想起要加的功能)    // 日志逻辑30行……}

“这叫‘大杂烩函数’。”春哥摇头,“它像一家什么都卖的杂货铺——既卖茶叶,又修钟表,还兼营快递代收。表面上方便,实则样样稀松。哪天你想改‘打印收据’的格式,得在这个300的函数里大海捞针;想重用‘计算账单’的逻辑,却发现它和‘更新库存’紧紧绑在一起,撕都撕不开。”

小蔡皱眉:“我好像写过这样的……”

“不丢人。”春哥笑了,“初学者谁没写过‘超级函数’?但要从‘能跑就行’进阶到‘写得漂亮’,就得学会‘拆’。”

正面教材:各司其职,井井有条

春哥调出重构后的代码:

// 好的设计:每个函数只做一件事voidshow_menu() { /* 专心显示菜单 */ }voidtake_order() { /* 专心处理点单 */ }floatcalculate_bill(OrderItem order[], int count) { /* 专心算钱 */ }voidupdate_inventory(OrderItem order[], int count) { /* 专心更新库存 */ }voidprint_receipt(float total, OrderItem order[], int count) { /* 专心打印 */ }voidwrite_log(char* action) { /* 专心记录日志 */ }

“看出来了吗?”春哥用茶针指着屏幕,“每个函数都像潮汕工夫茶里的一个‘专职工匠’——烧水的只管水温,冲茶的只管手法,品茶的只管鉴赏。这叫‘单一职责原则’(Single Responsibility Principle),是高内聚的核心体现。”

他进一步解释:“高内聚的好处,至少有三:

1.易读:函数名就是文档。一看calculate_bill,就知道它是算钱的,不会在里面找库存更新的代码。

2.易改:要改打印格式?直奔print_receipt,不用担心误伤计算逻辑。

3.易测:测试时,你可以单独测试calculate_bill算得对不对,不用先点一单、再更新库存、再打印收据,绕一大圈。”

生活比喻:潮汕菜馆的后厨分工

“你们去潮汕菜馆吃饭,”春哥打了个更生活的比方,“后厨一定是分工的:炒粿条的师傅不会跑去炖汤,卤鹅的师傅不会插手蒸鱼。为什么?因为每个师傅都把自己的手艺练到极致,整个后厨的效率和质量才高。”

“程序也一样。calculate_bill 这个‘算账师傅’,就应该把算价、打折、会员积分这些‘算账手艺’练到极致。如果它又跑去‘打印收据’(那是print_receipt师傅的活),又偷偷‘更新库存’(那是update_inventory师傅的事),那它就注定是个‘半吊子’,哪样都做不精。”

二、低耦合:茶壶与茶杯,相逢何必曾相识

“说完‘高内聚’,咱们说‘低耦合’。”春哥拿起茶壶,又拿起茶杯,“这壶和杯,要配合才能喝茶。但它们之间的‘联系’应该多紧密?”

阿雅试着回答:“壶倒茶,杯接茶……但壶不用知道杯是陶瓷的还是玻璃的,杯也不用知道壶里泡的是什么茶?”

“妙!”春哥眼睛一亮,“这就是低耦合的精髓——模块之间要能协作,但不要‘知根知底’,更不要‘你中有我、我中有你’。它们应该通过清晰、简单的‘接口’打交道,保持适度的距离感。”

反面教材:剪不断,理还乱

春哥又展示一段问题代码:

// module_a.cstatic int secret_counter = 0// 静态局部变量,本应隐藏voiddo_something() {    secret_counter++;    // ...}// module_b.c (另一个文件)extern int secret_counter; // 危险!直接暴露内部细节voidmanipulate_from_outside() {    secret_counter = 999// 外部模块直接修改内部状态    printf("我改了你的秘密计数器!\n");}

“这叫‘非法越界’。”春哥语气严肃,“module_b 像是一个不请自来的客人,不仅进了别人家的后院,还把人家的传家宝(secret_counter)给改了。两个模块‘耦合’得太紧,紧到module_b可以直接操作module_a的内部数据。”

“后果是什么?”他自问自答,“哪天module_a升级了,把secret_counter改了个名字,或者换了种实现方式——module_b立刻就崩了。更可怕的是,这种依赖关系往往是隐形的,写代码的人可能自己都没意识到,直到程序在某个月黑风高的夜晚突然崩溃。”

正面教材:君子之交,止于接口

春哥切换回好的设计:

// module_a.c(提供明确的“接口函数”)static int secret_counter = 0// 仍然隐藏intget_counter() { // 对外提供“只读”接口    return secret_counter;}voidincrement_counter() { // 对外提供“安全修改”接口    if (secret_counter < MAX_LIMIT) {        secret_counter++;    }}// module_b.c(只通过接口交互)voiduse_counter_properly() {    int current = get_counter(); // 通过接口读取    printf("当前计数:%d\n", current);    increment_counter(); // 通过接口修改    // 无法直接操作 secret_counter,也不知道它怎么实现的}

“看,”春哥指着代码,“现在module_b就像一个文明的客人——它想喝茶,不会自己冲进后厨翻茶叶,而是对掌柜说:‘劳驾,来一壶凤凰单丛。’至于这茶是放在哪个柜子、用什么水温冲泡的,它不必知道,也不必关心。”

“这就是低耦合。两个模块之间只有两条清晰可见的‘通道’(get_counterincrement_counter),其他的一切都被封装、隐藏起来。module_a可以随意改造内部实现(比如把secret_counterint改成long,或加上线程安全锁),只要这两个接口的行为不变,module_b就完全不受影响。”

生活比喻:潮汕祠堂的“公厅议事”

“咱们潮汕宗族,有个传统。”春哥说起文化,“族里大事要在祠堂的‘公厅’商议。各房各派派代表参加,但代表们只带‘意见’(参数)进去,带着‘决议’(返回值)出来。至于公厅里怎么讨论、怎么争论、谁说服了谁——那是厅内的事,外面的人不必知道,也不必插手。”

“低耦合,就像这个‘公厅’。它是模块之间唯一的正式交往渠道。外面的人(其他模块)不知道、也不应该知道厅内的具体讨论过程(内部实现)。这样,哪天公厅的议事规则改了(模块内部重构),只要输入输出的规矩(接口)不变,整个宗族(程序)的运转就不受影响。”

三、高内聚 低耦合:精密的潮汕钟表

“单独看‘高内聚’和‘低耦合’,可能还不够震撼。”春哥从怀中取出一块老怀表,黄铜外壳,珐琅表盘,“但两者结合,能创造出这样的艺术品。”

他打开表盖,露出里面精密的齿轮系统:“你看,每个齿轮(函数)都只做一件事——传递动力、控制速度、指示时间。这是‘高内聚’。”

“再看齿轮之间的关系,”他指着互相啮合但彼此独立的齿,“它们通过标准的齿形(接口)咬合,但每个齿轮都是完整的个体,可以单独拆下、维修、替换。这是‘低耦合’。”

“这样的钟表,”春哥合上表盖,“走时精准,经久耐用,维修方便。我们的程序,也应该如此。”

一个完整的好例子:茶铺“智能推荐系统”

春哥设计了一个综合案例:

// ========== 高内聚的模块 ==========// 模块1:只负责“读取用户历史订单”OrderHistory* read_user_history(int user_id){    // 专心从数据库/文件读取历史    // 不关心数据怎么用}// 模块2:只负责“分析口味偏好”TasteProfile* analyze_taste(OrderHistory* history){    // 专心分析:喜欢清香还是浓香?常买哪种茶?    // 不关心数据从哪里来、到哪里去}// 模块3:只负责“匹配推荐茶品”TeaItem* recommend_tea(TasteProfile* profile, TeaItem menu[], int count){    // 专心做匹配算法    // 不关心分析结果怎么来的,也不关心推荐结果怎么展示}// 模块4:只负责“格式化推荐结果”charformat_recommendation(TeaItem* tea, TasteProfile* profile){    // 专心生成好看的建议文案    // 例如:“根据您常买凤凰单丛,推荐试试蜜兰香,同属乌龙,花香更显”    // 不关心匹配算法细节}// ========== 低耦合的协作 ==========// 主函数:像导演,只负责“调度”voidshow_recommendation(int user_id){    // 1. 获取历史(通过接口)    OrderHistory* history = read_user_history(user_id);    // 2. 分析口味(通过接口)    TasteProfile* profile = analyze_taste(history);    // 3. 匹配推荐(通过接口)    TeaItem* recommendation = recommend_tea(profile, global_menu, MENU_SIZE);    // 4. 格式化展示(通过接口)    char* message = format_recommendation(recommendation, profile);    printf("%s\n", message);    // 5. 释放资源(每个模块管理自己的内存)    free_history(history);    free_profile(profile);    free_message(message);    // 注意:recommendation指向全局菜单,不需要单独释放}

“看明白这‘四步舞’了吗?”春哥讲解,“每个演员(函数)都专业而专注(高内聚),它们之间不私下传纸条,只通过导演(主函数)给的明确指令(接口调用)协作(低耦合)。”

“如果明天我想换一种推荐算法,”他继续说,“只需要修改recommend_tea模块的内部实现,其他三个模块完全不动。如果我想把推荐结果通过微信消息发送,而不是打印在屏幕,只需要写一个新的format_recommendation模块(比如format_for_wechat),替换掉原来的——其他三个模块还是不动。”

“这就是模块化设计的威力。”春哥总结,“高内聚让每个部件可靠,低耦合让整个系统灵活。”

四、如何在实践中修炼心法?

茶汤已凉,但三人的眼睛却越来越亮。小蔡问:“春哥,道理懂了,可实际写代码时,怎么判断自己写得够不够‘高内聚、低耦合’?”

“好问题。”春哥竖起三根手指,“我给你们三条‘自检口诀’。”

口诀一:函数命名不超过“和”

“如果一个函数的名字里出现了‘和’、‘以及’、‘然后’这样的词,比如process_order_and_update_inventory_and_print_receipt()——立刻警铃大作!它很可能干了太多事,内聚性不够高。”

“好的函数名应该像一个动词短语:calculate_billvalidate_inputsort_array——动作单一,目标明确。”

口诀二:参数列表不超过“一掌”

“如果一个函数的参数超过五个(一掌之数),就要想想:是不是让它知道得太多了?参数多,往往意味着这个函数和外界耦合太紧,要依赖太多外部信息才能工作。”

“试着把相关的参数打包成结构体(春哥注:结构体相关内容在第六盏学习),或者看看能不能拆分成更小的函数。”

口诀三:改动时不必“牵一发而动全身”

“修改一个函数时,如果发现必须跟着修改其他好几个不相关的函数——那说明耦合度太高了。”

“理想的状态是:修改一个模块,只影响它自己;最多影响直接调用它的少数几个模块。那种‘改一处,动全身’的代码,是耦合过紧的典型症状。”

五、心法的终极境界:写人能读的代码

春哥最后说了一段话,让三人沉默良久:

“你们知道吗?‘高内聚、低耦合’这些原则,最深的受益人不是计算机,而是未来的你,和要读你代码的同事。”

“计算机不在乎你的代码是‘面条’还是‘瑞士表’——它都能执行。但人呢?三个月后,你自己回头看这段代码,还能看懂吗?别人接手你的项目,能顺利吗?”

写高内聚、低耦合的代码,是一种尊重——尊重他人的时间,也尊重未来的自己。你把逻辑拆解得清清楚楚,把接口定义得明明白白,就是在说:‘这里我认真思考过,你可以放心使用、放心修改。’”

“这才是模块化心法的终极境界:写人能读、人能懂、人能改的代码。”

炉火渐微,茶馆里的暖光在三人脸上明明灭灭。春哥将最后几滴茶汤点入杯中,起身推开一扇窗。

夜风涌入,带着韩江水汽和远处隐约的市声。广济桥的灯火在江面上拉出长长的、破碎的光影。

春哥的声音混在风里,“今夜之后,希望你们写下的每一行代码,都不再是孤独的字符,而是有组织、有纪律、有呼吸的活系统。”

“记住:函数是你的‘将士’,模块是你的‘军团’。高内聚是军纪,低耦合是兵法。用好它们,你才能在程序江湖里,真正地——运筹帷幄,决胜千里。”

窗外,潮声如旧。但三人知道,有些东西,已经不一样了。

第九回:自顶向下、逐步求精——潮汕木雕的“先有大局,再雕细部”

茶过三巡,夜色渐浓。阿明盯着重构后的“猜数字”代码,忽然抬头问:“春哥,你教我们把大程序拆成小函数,这法子好。可面对一个全新的大问题,怎么知道该拆成哪些函数呢?从哪下第一刀?”

春哥微微一笑,从茶柜底层取出一件未完工的潮汕木雕——一只展翅的凤凰,已粗具轮廓,细节尚待雕琢。

“问得好。这就要说到模块化设计的方法论了。”他将木雕置于茶台中央,“看这木雕。老师傅不会一上来就雕凤凰的眼睛、羽毛,而是分三步走:先画整体图样(设计),再劈出大形(粗分),最后精雕细琢(细化)。编程同理,这叫——”

自顶向下

逐步求精

第一层:俯瞰全局,定“总谱”

“面对复杂问题,初学者常犯的错是‘低头就写’。”春哥在白板上画出一个混乱的流程图,“想到哪写到哪,最后代码成了一团乱麻。”

“正确做法是像作曲——先定总谱,再写分谱。”他切换投影,展示一个“潮汕工夫茶智能冲泡系统”的需求:

需求:设计一个智能茶艺系统,能根据茶叶类型自动推荐水温、冲泡时间,记录用户偏好,并生成冲泡日志。

“第一步,不要急着写代码,而是用自然语言描述主流程。”春哥写下:

// 第一步:用自然语言描述主流程(伪代码)1. 显示欢迎界面2. 让用户选择茶叶类型3. 根据茶叶类型获取推荐参数(水温、时间)4. 开始冲泡(倒计时、实时提示)5. 记录本次冲泡数据(用户评分、实际参数)6. 生成冲泡日志7. 询问是否继续

“这就像木雕的‘设计图’,虽然粗糙,但骨架已立。”春哥说,“谭浩强老师在教材第7章开篇就强调:‘先整体,后局部;先抽象,后具体’。这就是‘自顶向下’——从最顶层的main函数开始思考,逐步分解子任务。”

第二层:庖丁解牛,分“模块”

有了总谱,下一步是将每个步骤映射为一个或多个函数。”春哥在伪代码旁标注:

// 第二步:将步骤映射为函数(顶层设计)intmain() {    1. show_welcome();                     // 显示欢迎界面    2. tea_type = select_tea_type();       // 用户选择    3. params = get_recommendation(tea_type); // 获取推荐参数    4. brew_tea(params);                   // 执行冲泡    5. record_data(tea_type, params);      // 记录数据    6. generate_log();                     // 生成日志    7. if (ask_continue()) goto step 2;    // 循环}

“看,main()在只有7行‘调度语句’,具体活计都交给专业函数了。”春哥指着代码,“这就是‘逐步求精’的第一层求精——把大问题分解为几个中等问题。”

第三层:深入细节,雕“筋骨”

“但每个中等问题可能还太复杂。”春哥放大brew_tea()函数,“比如‘冲泡’这一步,包含:预热茶具、量取茶叶、注水、计时、出汤等多个动作。怎么办?继续分解。”

// 第三步:对复杂函数进一步分解(第二层求精)voidbrew_tea(TeaParams params) {    preheat_utensils(params.temperature);   // 预热    measure_tea(params.tea_amount);         // 量茶    pour_water(params.temperature);         // 注水    start_timer(params.brew_time);          // 计时    wait_and_prompt();                      // 等待并提示    serve_tea();                           // 出汤}

“如此层层分解,直到每个函数都足够简单、单一。”春哥总结,“这个过程像潮汕木雕——先砍出凤凰的大轮廓(顶层设计),再雕出翅膀、尾羽的形状(模块划分),最后精修每一片羽毛的纹路(函数实现)。”

实战案例:从“茶铺账房”看逐步求精

春哥带三人回顾第二盏的“茶铺账房系统”,用逐步求精法重新设计:

// 第一层:主流程(最顶层)1. 初始化系统(加载菜单、库存)2. 显示主菜单3. 根据用户选择进入相应功能:   - 点单   - 结账   - 库存管理   - 会员管理4. 循环直到退出// 第二层:细化“点单”功能点单流程:  1. 显示茶单  2. 用户选择茶品和数量  3. 验证库存是否充足  4. 加入购物车  5. 询问是否继续点单  6. 返回购物车// 第三层:细化“显示茶单”显示茶单:  1. 读取茶品数组  2. 遍历数组,格式化输出:     - 编号     - 名称     - 价格     - 库存状态  3. 添加美观的分隔线

“每一步分解,都让问题更具体、更可控。”春哥说,“这就是谭浩强老师强调的‘逐步求精、逐步细化’。当你把大问题分解到每个函数只有20-30行代码时,你就掌握了模块化的精髓。”

自顶向下的优势:像将军布阵,先谋全局

“自顶向下设计有三大好处。”春哥竖起三根手指:

1. 避免过早陷入细节

“初学者常一头扎进某个局部难题(比如‘怎么计算折扣最优惠’),结果花了三天,回头发现整体架构走不通。自顶向下让你先看清全貌,确保大方向正确。”

2. 自然形成模块接口

“在分解过程中,函数间的输入输出(接口)会自然浮现。比如get_recommendation()需要知道茶叶类型,返回水温、时间——这就是它的‘职责范围’。接口清晰,耦合度自然低。”

3. 便于团队协作

“木雕大师傅负责整体构图,徒弟们分雕头、翅、尾。编程也一样——架构师设计顶层,不同程序员并行实现不同模块。没有顶层设计,团队就会像没指挥的交响乐团,各吹各的调。”

一个生动的比喻:潮汕祠堂的营建

“咱们潮汕建祠堂,”春哥又举一例,“不是今天砌堵墙、明天盖片瓦。而是:

(1)风水先生选址、定朝向(需求分析、顶层设计)

(2)老师傅画出‘厝局图’(架构设计、模块划分)

(3)石匠、木匠、瓦匠各领图纸(分工实现)

(4)最后‘合脊’——将各部分严丝合缝对接(集成测试)

“如果石匠不等图纸就开工,很可能柱子雕好了,发现尺寸对不上梁架。”春哥敲敲茶台,“编程同理。先设计,后编码;先接口,后实现——这是血的教训换来的经验。”

练习:用自顶向下法设计“韩江夜话学习系统”

春哥给出新题目:“假设我们要为《韩江夜话》开发一个学习辅助系统,帮助读者温习前五盏内容。用自顶向下法,你会怎么设计?”

三人讨论后,阿雅提出方案:

// 顶层设计:1. 显示系统首页(选择:复习、测验、进度查看)2. 复习模式:按章节浏览知识点3. 测验模式:随机出题,即时批改4. 进度跟踪:记录学习时长、正确率5. 退出系统// 第二层:细化“测验模式”测验流程:  1. 选择测验范围(第1-5盏)  2. 从题库随机抽取N道题  3. 逐题显示,接受用户答案  4. 即时反馈对错,显示解析  5. 测验结束,显示总分和错题回顾// 第三层:细化“抽题函数”抽题算法:  1. 根据范围筛选题库  2. 随机打乱题目顺序  3. 取前N道题  4. 返回题目数组

“很好。”春哥赞许,“自顶向下的思维,让你面对任何新项目都不会手忙脚乱。记住这个心法:先有大局,再雕细部;先画图纸,再动刀斧。”

窗外的潮声不知何时停了。茶馆里只剩下炉火的微响,和笔尖划过纸面的沙沙声。

第十回:模块化设计与面向对象设计的比较——潮汕“四点金”与苏州园林的对话

夜更深了。小蔡忽然问:“春哥,我听说现在流行‘面向对象编程’,JavaPython都在用。咱们学的C语言模块化,和面向对象是什么关系?哪个更好?”

春哥没有直接回答,而是走到墙边,打开两幅画轴。

左边一幅是潮汕“四点金”民居的剖面图——方正规矩,厅房分明,功能清晰。右边一幅是苏州园林的俯瞰图——亭台错落,曲径通幽,移步换景。

“模块化设计,像潮汕‘四点金’。”春哥指着左图,“每个房间(函数)功能明确:正厅待客,厢房住人,厨房炊煮,井台洗漱。房间之间通过走廊(接口)连接,结构清晰,便于扩建。”

“面向对象设计,像苏州园林。”他转向右图,“它不按‘功能’分区,而是按‘事物’组织:这是‘假山’对象,那是‘池塘’对象,那是‘亭子’对象。每个对象既有状态(数据),又有行为(方法),自成一体,相互协作。”

一、核心哲学:功能分割 vs 事物抽象

模块化(C语言风格):按“干什么”划分

模块化设计的核心是功能分解。”春哥回到茶台,画出“茶铺系统”的模块图:

茶铺系统├── 点单模块 (负责:接收订单)├── 计算模块 (负责:算钱打折)├── 库存模块 (负责:管理货物)└── 打印模块 (负责:输出收据)

“每个模块是一组相关函数的集合,专注于一个功能领域。数据(如订单、库存)往往作为‘共享资源’,在模块间传递。”

他写下C语言的典型代码:

// 数据结构和函数分离typedef struct {    int id;    char name[20];    float price;} TeaItem;// 操作数据的函数(分散在各个模块)voidadd_to_order(TeaItem tea, int quantity);  // 点单模块floatcalculate_discount(Order* order);        // 计算模块voidupdate_stock(TeaItem tea, int sold);      // 库存模块

“数据(TeaItem)是‘被动’的,函数是‘主动’的操作者。这就像‘四点金’——房子(数据)是固定的,人在不同房间(函数)间移动,完成不同活动。”

面向对象(C++/Java风格):按“是什么”封装

面向对象设计的核心是数据封装。”春哥画出对象视角的“茶铺系统”:

茶铺系统├── 茶品对象 (属性:名称、价格、库存;方法:售卖、补货)├── 订单对象 (属性:茶品列表、总价;方法:添加项目、计算总额)├── 会员对象 (属性:姓名、积分;方法:消费、升级)└── 打印机对象 (方法:打印收据、打印报表)

每个对象把数据和对数据的操作绑在一起,形成一个‘小世界’。对象之间通过‘发送消息’(调用方法)协作。”

他写出C++风格的代码:

// 数据和方法封装在一个类中class TeaItem {private:    string name;    float price;    int stock;public:    // 方法:操作自己的数据    voidsell(int quantity) {        if (stock >= quantity) {            stock -= quantity;            cout << "售出" << quantity << "份" << name << endl;        }    }    voidrestock(int amount) {        stock += amount;    }    // 获取信息的接口    stringgetName() { return name; }    floatgetPrice() { return price; }};

“这里TeaItem不仅知道自己的信息(名称、价格),还知道能对自己做什么(售卖、补货)。这就像苏州园林里的‘假山’——它不仅是景观,还知道怎么让自己更美(苔藓养护)、怎么与池水互动(倒影效果)。”

二、关键特性对比:四大差异

春哥列出对比表:

特性

模块化设计 (C语言)

面向对象设计 (C++/Java)

组织单元

函数 (按功能分组)

/对象 (按事物封装)

数据与操作

分离 (数据被动,函数主动)

封装在一起 (对象自主)

代码复用

函数库 (复用算法)

继承、组合 (复用设计和行为)

核心机制

函数调用、指针

类、对象、继承、多态

差异一:封装程度

“模块化也有封装——用static函数隐藏内部细节。但数据的封装较弱,常需全局变量或传递指针。”春哥举例:

// C模块化:数据暴露较多extern TeaItem g_tea_menu[MAX];  // 全局数组,谁都能改// C++面向对象:数据隐藏较好class TeaManager {private:    TeaItem menu[MAX];  // 私有,外部无法直接访问public:    TeaItem* getTea(int id){  // 通过公有方法访问        if (id >= 0 && id < MAX) return &menu[id];        return NULL;    }};

“面向对象的封装更严格,有助于减少意外修改。”

差异二:复用机制

“模块化复用函数,就像工具箱里挑扳手。”春哥说,“面向对象复用‘类’,则像乐高积木——不仅可以复用单个积木(类),还能复用搭建模式(继承)。”

// C模块化:复用函数#include "math_utils.h"  // 包含各种数学函数result = calculate_area(radius);// C++面向对象:复用类设计class Animal { /* 基础类 */ };class Bird : public Animal { /* 继承并扩展 */ };class Fish : public Animal { /* 另一种继承 */ };

“继承让相似事物能共享代码,这是模块化较难优雅实现的。”

差异三:设计思维

“模块化思维是过程式的:关注‘怎么做’——先做什么,再做什么。”春哥画出流程图。

“面向对象思维是概念式的:先识别系统中的‘事物’(对象),再定义它们如何互动。”他画出对象协作图。

“比如设计‘茶铺系统’,模块化思维会想:‘系统要完成哪些功能步骤?’面向对象思维会想:‘系统中有哪些实体?茶品、订单、会员……它们各自有什么属性和行为?’”

三、一个具体例子:两种方式实现“潮汕粿品店系统”

春哥带三人用两种风格实现同一个系统。

模块化实现(C风格)

// 数据结构定义typedef struct {    char name[30];    float price;    int stock;} GuoTin;// 全局数据(模块间共享)GuoTin guoTinList[100];int guoTinCount = 0;// 功能模块voidloadGuoTinData() { /* 从文件加载数据 */ }voiddisplayMenu() { /* 显示菜单 */ }voidtakeOrder() { /* 处理订单 */ }voidupdateStock(int id, int sold) { /* 更新库存 */ }voidgenerateReport() { /* 生成报表 */ }// 主函数协调intmain() {    loadGuoTinData();    while(1) {        displayMenu();        int choice = getUserChoice();        switch(choice) {            case 1: takeOrder(); break;            case 2: generateReport(); break;            // ...        }    }}

面向对象实现(C++风格)

// 类定义:每个实体是一个类class GuoTin {private:    string name;    float price;    int stock;public:    voidsell(int quantity)/* 售卖方法 */ }    voidrestock(int amount)/* 补货方法 */ }    voiddisplayInfo()/* 显示信息 */ }};class Order {private:    vector<pair<GuoTin*, int>> items;  // 订单项    float total;public:    voidaddItem(GuoTin* guoTin, int qty)/* 添加项目 */ }    voidcalculateTotal()/* 计算总额 */ }    voidprintReceipt()/* 打印收据 */ }};class Shop {private:    vector<GuoTin> inventory;    vector<Order> todayOrders;public:    voidrun()/* 主循环 */ }};// 主函数简洁intmain(){    Shop myShop;    myShop.run();    return 0;}

“看出区别了吗?”春哥对比两段代码,“C版本像说明书:第一步做什么,第二步做什么。C++版本像组织架构图:有哪些部门,每个部门负责什么。”

四、优劣与适用场景:没有最好,只有最合适

“那么,哪个更好?”春哥问。

三人沉默。春哥自己回答:“没有银弹,只有适合。”

模块化的优势

1. 简单直接:对于流程明确、算法密集的程序(科学计算、操作系统内核),模块化更直观。

2. 性能高效:C语言的函数调用开销小,无对象创建、虚函数等额外成本。

3. 资源受限环境:嵌入式系统、单片机,内存宝贵,模块化更轻量。

4. 已有大量库:C标准库、Linux系统调用都是模块化风格,生态成熟。

“比如咱们前几盏的‘猜数字游戏’、‘成绩统计’,用模块化实现简单清晰。硬要用面向对象,反而过度设计。”

面向对象的优势

1. 复杂系统建模:GUI程序、游戏、企业系统,对象模型更贴近现实。

2. 代码复用和扩展:通过继承、多态,能优雅地添加新功能。

3. 团队协作:类作为契约,不同程序员负责不同类,接口清晰。

4. 维护性:封装减少意外修改,继承使修改变得局部化。

“比如要开发一个‘潮汕茶文化虚拟博物馆’,里面有各种茶叶、茶具、人物角色,它们要互动、要变化——面向对象就更合适。”

五、殊途同归:优秀设计的共同原则

春哥泡了最后一壶茶,茶香在深夜格外清冽。

“其实,两种范式在底层是相通的。”他说,“无论模块化还是面向对象,优秀的代码都遵循高内聚、低耦合。”

“在模块化中,高内聚是‘一个函数做好一件事’;在面向对象中,高内聚是‘一个类职责单一’。在模块化中,低耦合是‘通过清晰接口交互’;在面向对象中,低耦合是‘对象间松散关联’。”

“就像潮汕‘四点金’和苏州园林——建筑风格不同,但都追求功能合理、布局和谐、与环境相融。”

六、给初学者的建议:先通一门,再窥他山

“你们现在学C语言模块化,不要觉得‘落后’。”春哥正色道,“模块化是面向对象的基础。不理解函数、数据分离的痛苦,就体会不到封装的妙处;没写过全局变量带来的bug,就不懂为什么需要私有成员。”

“先精通模块化,把‘分而治之’的思维刻进骨子里。等你将来学JavaPython时,会发现:面向对象不过是更高级的模块化——它把数据和函数打包成更大的‘模块’(类),并提供了继承、多态等更强大的组织工具。”

“就像学功夫——先扎马步、练拳架(模块化),再学套路、练内劲(面向对象)。马步不稳,套路再花哨也是花拳绣腿。”

茶馆外传来第一声鸡鸣。东方既白,韩江上泛起鱼肚白。

春哥收起两幅画轴:“记住,编程范式不是信仰,而是工具。潮汕师傅既会建‘四点金’,也会欣赏苏州园林。好的程序员,应该根据问题选工具,而不是被工具限制思维。”

三人起身,带着满脑子的“四点金”与“园林”、“功能”与“对象”,走入渐亮的晨光中。

尾声:江声入海,道器相生

茶尽灯残,炉火渐冷。

春哥没有立即收拾茶具,而是缓步走到窗前,望着窗外缓缓流动的韩江。江心渔火已灭,广济桥的轮廓在晨光微熹中渐渐清晰,像一道横卧于水面的墨痕。

阿明、阿雅、小蔡收拾好笔记,却迟迟未动。他们知道,这一夜的茶,喝到了心底最深处。

“你们看这韩江。”春哥忽然开口,声音平静如江面,“它从凤凰山发源,一路汇集万千溪流,过山峡、经平原,最后在这里——潮州古城前,变得宽阔而深沉。”

他转身,目光扫过三人:“前面八回,咱们学的其实也是一条江。”

一、一条江的三种看江法

“第一回到第四回,咱们在看江的局部。”春哥复盘,“看一滴水如何组成(数据类型),看水流如何转向(控制结构),看江水如何蓄积成潭(数组与指针)——这是‘见木’。”

“今晚的第五回,咱们在学看江的脉络。”他指着桌上那些被拆解、重构的代码,“如何把一条大江分成支流(函数模块),支流间如何交汇又保持独立(高内聚低耦合),如何从源头规划整条水系(自顶向下)——这是‘见林’。”

“而后面的内容,”春哥顿了顿,“咱们将看到了江的可能性。同一条韩江,在潮汕人眼中是‘母亲河’,滋养田园;在船工眼中是‘黄金水道’,承载货运;在诗人眼中是‘灵感之源’,流淌诗意。”

“模块化是潮汕‘四点金’——方正规矩,户户分明,适合安居。面向对象是苏州园林——移步换景,处处成画,适合游赏。没有孰优孰劣,只有是否合宜。”

二、从“匠”到“师”的渡口

春哥回到茶台,最后一次提起已凉的茶壶,为三人斟上最后一杯——不是茶,是白水。

“喝一口。”他说。

清水入喉,无香无味,却解渴。

“前几夜,我教你们的是‘茶’——各种技法、规矩、心法。今夜最后这杯,是‘水’。”春哥放下壶,“编程的至高境界,不是记住多少语法,不是熟练多少套路,而是在合适的时候,选择合适的方法,解决真正的问题。”

他竖起三根手指:

第一重境界,是‘用器’。学会函数怎么写、指针怎么用,就像学徒学会用刻刀、用刨子。这是基础,必须扎实。

第二重境界,是‘知器’。明白为什么用这个函数结构,为什么这样传参,知道模块化与面向对象各自的妙处与局限。像师傅懂得每样工具的来历、特性、最佳用途。

第三重境界,是‘忘器’。手中无剑,心中有剑。面对问题时,不再机械套用‘该用模块化还是面向对象’,而是从问题本身出发,自然生长出最适合的解法。像大师傅雕木,心中早有凤凰,刀只是手的延伸。”

三、程序江湖的“不变”与“万变”

窗外,天光渐亮,江面泛起金鳞。

“技术会变。”春哥声音低沉,“今天学C语言,明天可能有新语言;今天用模块化,明天流行新范式。但有些东西,比技术活得长久。”

“问题分解的能力不会过时——再复杂的问题,总能拆解成可处理的小块。这是自顶向下的智慧。

抽象思维的能力不会过时——透过表象看本质,找到变化中的不变。这是高内聚低耦合的根基。

系统思考的能力不会过时——既见树木,又见森林;既懂局部优化,又知整体协调。这是模块化与面向对象共同追求的。”

他指着江对岸的笔架山:“你看那山,千万年立在那里。山下韩江,水却时时不同——春潮夏汛,秋清冬浅。编程之道,如山;编程之术,如水。道是根基,术是流变。”

四、临别赠言:三个“守住”

三人起身,春哥送至楼梯口,忽然停步:

“最后送你们三句话,算是这五盏茶的‘茶渣卦’——潮汕人说,茶渣倒置杯底,能看出运势。我不看运势,只看本心。”

第一,守住‘分而治之’的初心。 无论将来写什么语言、做什么项目,遇到复杂问题,先拆解。这是程序员的元能力。

第二,守住‘高内聚低耦合’的尺度。 写代码如做人——自己该做的事做好(高内聚),与他人相处有边界、有接口(低耦合)。这是好程序也是好人生的准则。

第三,守住‘道器相生’的清醒。 不迷信任何技术范式,不轻视任何基础功夫。工具是手的延伸,思维是人的光芒。用工具,但不被工具所用。”

他推开门,晨风涌入,带着江水的气息。

“回去吧。天亮了。”

五、江声长流,灯火不灭

三人走下楼梯,踏出茶馆。回头望去,二楼的灯还亮着——不是昨夜那种温暖的黄,而是清冷的白,像黎明前最后一颗星。

阿明忽然想起什么,转身喊:“春哥,下一盏什么时候?”

窗口现出春哥的身影,背后是那片渐亮的天空。他没有回答,只是挥了挥手,然后关上了窗。

但那盏灯,依然亮着。

走在韩江边,晨光已铺满水面。广济桥上的行人渐多,卖早点的摊贩开始生火,第一班渡船鸣笛离岸。

“你们说,”小蔡忽然问,“春哥最后那杯白水,是什么意思?”

阿雅想了想:“是让我们回归本源吧。茶再好,本质还是水。技术再炫,本质还是解决问题。”

阿明看向江面,江心一道航迹,正缓缓散开:“我觉得,他是说——五盏茶喝完了,该自己去找水源了。”

江声浩荡,奔流赴海。

他们不知道,茶馆二楼,春哥正站在窗前,看着三个年轻身影渐行渐远。桌上,五只若深杯倒扣在茶盘里,杯底残留的茶渣,在晨光中显出奇异的纹路。

春哥没有去看那些纹路。他提起笔,在《C程序设计》扉页上,写下四行字:

第一盏立魂魄:算法为骨,数据为肉

第二盏塑皮囊:类型为基,运算为脉

第三盏通经络:结构为络,循环为息

第四盏成军阵:数组为营,指针为令

第五盏开宗派:函数为帅,模块为疆

——夜话五盏毕,江湖自此宽

合上书时,第一缕阳光正好照进窗棂,落在“江湖自此宽”五个字上。

楼下传来早客的脚步声,新一天的茶市开始了。

春哥吹熄那盏守了一夜的灯,青烟袅袅升起,在光柱中缓缓旋转,最终消散无痕。

只有韩江,依旧在窗外流淌。潮起潮落,不因一盏灯的明灭而改变节奏。

但有些东西,已经改变了。

那些关于程序如何结构、问题如何拆解、思维如何成长的种子,已在三颗年轻的心里埋下。它们会随着时间发芽,随着实践生长,最终长成各自的模样——或许是严谨的模块森林,或许是灵动的对象花园,或许是完全不同的风景。

而这,正是“夜话”真正的意义:

不是传授固定的答案,而是点燃思考的火种;不是绘制唯一的地图,而是给予寻找路径的勇气。

江声入海,道器相生。(第五盏终)

欢迎朋友们阅读、转发,提一提建议,在讨论区展开更深入讨论。

本公众号往期文章

2026.02.01:韩江夜话第四盏:C语言之内存的江湖——数组、指针与寻址的茶道
2026年01月:“大眼鱼”公众号2026年01月的文章列表
2025年12月:“大眼鱼”公众号2025年12月的文章列表
2025年11月:“大眼鱼”公众号2025年11月的文章列表
2025年10月:“大眼鱼”公众号2025年10月的文章列表
2025年09月:“大眼鱼”公众号2025年09月的文章列表

欢迎关注我们的公众号“大眼鱼”

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-08 02:55:40 HTTP/2.0 GET : https://f.mffb.com.cn/a/471580.html
  2. 运行时间 : 0.217838s [ 吞吐率:4.59req/s ] 内存消耗:4,719.43kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=fedff471f96d8ef83d96ae39bec0758d
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000939s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001431s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000713s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.001665s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001399s ]
  6. SELECT * FROM `set` [ RunTime:0.001510s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001550s ]
  8. SELECT * FROM `article` WHERE `id` = 471580 LIMIT 1 [ RunTime:0.009391s ]
  9. UPDATE `article` SET `lasttime` = 1770490540 WHERE `id` = 471580 [ RunTime:0.009757s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 65 LIMIT 1 [ RunTime:0.000878s ]
  11. SELECT * FROM `article` WHERE `id` < 471580 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.005488s ]
  12. SELECT * FROM `article` WHERE `id` > 471580 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.003695s ]
  13. SELECT * FROM `article` WHERE `id` < 471580 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.015022s ]
  14. SELECT * FROM `article` WHERE `id` < 471580 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.005638s ]
  15. SELECT * FROM `article` WHERE `id` < 471580 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.008048s ]
0.219533s