肥宅钓鱼网
当前位置: 首页 钓鱼百科

c语言函数程序设计的思路(一起学C程序设计第七课)

时间:2023-07-02 作者: 小编 阅读量: 2 栏目名: 钓鱼百科

递归调用所谓函数的递归调用就是返回值里有部分构成是函数自身。在写递归函数需要注意的点是递归是有限的,一定要有终止的逻辑,让递归函数不再调用自身从而结束递归;否则无限递归下去也是一种“死循环”。static局部变量当static关键字作用在内部变量上,意味着该局部变量不随着所在函数调用完毕后释放内存,即存储方式由动态存储变为静态存储。

C语言函数

在使用visual studio练习编程的时候,可能会出现这样的情况:做了很多的语法练习,既有单独一行语句的练习,也有解决一个课后习题这样一个完整的程序,它们都在同一个main()主函数中,在调试中可能要注释掉一些代码,单独去看当前的一段代码,这样操作有点繁琐,而且主函数越来越“臃肿”显得结构混乱,那怎样能解决这个问题呢?如果把要调试的这段代码独立出来,和其他代码互不影响,需要的时候再去主函数里调用,这样就会使得练习调试方便的多,这段独立代码的形式就是今天的主题——函数。

注意,请认真学习完《C程序设计(第五版)》第七章后再阅读本文会有更大的收获。

函数

我们可以把函数理解成一个迷你型的程序,它有输入输出,也实现了某些算法逻辑。

函数输入——参数

一般基础类型参数,如整型、浮点、字符等,实参的值传递给形参,它们互不影响。

数组类型的参数,由于数组实参传递的是数组首个元素的存储地址,所以形参实际上还是指向同一个数组的,如果形参的值发生变化,相应的实参也会受影响。

函数输出——返回值

通常,我们会统一函数定义的数据类型和return值的数据类型,应该避免“强制数据类型转换”的情况,让程序更加的严谨。

当然,严谨也就意味着失去一些“自由度”,其他的语言比如PHP、Python没有这样的要求,它们甚至没有函数数据类型这样的定义。毕竟每种语言的设计初衷和理念都不太相同,在今后的学习使用中去体会设计者的思路。

函数体

函数体是函数的核心,函数功能的实现都在这里进行编码。在构建函数体的过程中,推荐的做法是先写出框架,再补齐逻辑代码。比如函数体内有一个for循环,那就先出for循环的框架,然后再写continue以及break跳出循环的判断节点,最后再来补齐这里面的逻辑。

函数调用

自定义函数调用

当前源文件调用,在自定义函数之前调用要声明,之后则不用声明。

其他源文件调用,要用extern进行声明。

系统函数调用

使用系统函数前要包含进来相应的头文件,比如使用数学函数sqrt()要在文件开头包含math.h头文件:#include <math.h>,前面还用过字符串相关的系统函数,以及最普遍的系统标准输入输出函数。

嵌套调用

函数A的函数体内调用函数B,函数B的函数体内调用函数C……需要注意的是不能“反调用”,比如A调用B,B又调用了A,或者A调用B,B调用C,C里又调用A,这样就形成了“死循环”。

递归调用

所谓函数的递归调用就是返回值里有部分构成是函数自身。在写递归函数需要注意的点是递归是有限的,一定要有终止的逻辑,让递归函数不再调用自身从而结束递归;否则无限递归下去也是一种“死循环”。

如果递归过程中要存储一些过程中的数据,这时候可以使用static局部变量使得静态化存储,或者以形参数组的形式在函数内部使用。

函数作用域

C语言中,默认自定义的函数是外部函数,即全局函数,可以被其他源文件调用;而内部函数则是相对于函数所在的源文件来界定的,只能在当前源文件内被调用。

所以,函数作用域最小范围是其所在的源文件,除此之外就是作用于全局(所有源文件)。

变量作用域和存储类型

书中的表7.2总结得很全面,了解作用域和存储类型,使得我们在写程序的时候更加严谨规范,不随意定义全局的变量,使程序数据更加“安全”。

对于初学者来说,这些偏理论的知识往往都比较难理解,也很枯燥。笔者推荐在学习的过程中结合visual studio进行实操练习,通过代码辅助增强对这些理论知识的理解;做到知其然也知其所以然,培养好的编程习惯,从底层了解变量的作用域和存储特性,也为学习其他的高级语言做铺垫。

static关键字

static外部变量/外部函数

当static关键字作用在外部变量/外部函数上,意味着该外部变量/外部函数只能在当前的源文件内进行使用,无法跨文件调用。

static局部变量

当static关键字作用在内部变量上,意味着该局部变量不随着所在函数调用完毕后释放内存,即存储方式由动态存储变为静态存储。

实战编程

首先,看一下练习编程的目录结构和多个源文件互相调用运行的规则,顺便把刚学习的知识运用上。

练习目录结构

在源文件目录下创.c结尾的源文件,程序运行的入口文件是main.c,从main()函数开始执行。笔者把每一章节的程序练习单独建立对应的源文件如lesson7.c,在lesson7.c里再创建lesson7()函数,这个函数相当于它所在源文件的主函数,然后在main()函数里调用lesson7(),具体结构如下:

注意,这里的单个源文件中定义的练习函数都加了static关键词表示仅在当前源文件中调用,这样避免与其他源文件同名时造成冲突。

写两个函数分别求两个整数的最大公约数和最小公倍数

这个题目在之前的练习中已经做过,具体算法不再分析,参考一下:

static int maxCommonDivisor(int a, int b) {int divisor = 1;for (int i = 1; i <= a; i){if (a % i == 0 && b % i == 0) {divisor = i;}}return divisor;}static int minCommonMultiple(int a, int b) {int multiple = 1;int min = a > b ? b : a;int i = 1;while (1){multiple = min * i;if (multiple % a == 0 && multiple % b == 0) {return multiple;}i;}}

写一个函数,使给定的一个3x3的二维数组转置,即行列互换。

这个题目很简单,比较直接的实现思路是把给定的数组拷贝一份,然后循环把数组的元素重新赋值即可,赋值逻辑为:array[i][j] = arrayCopy[j][i]。为了加深理解函数的嵌套调用,我们增加两个函数:一个是输出二维数组函数,做转置前后的输出对比;另一个是拷贝二维数组的函数,供转置函数去调用,代码参考如下:

static void transpose(int array[3][3]);static void printfArray(int array[3][3]);int array[3][3] = {{1, 2, 3},{4, 5, 6},{7, 8, 9}};printfArray(array, 3);transpose(array);printf("=====================\n");printfArray(array, 3);static void printfArray(int array[][3], int length) {for (int i = 0; i < length; i) {for (int j = 0; j < length; j){printf("%d\t", array[i][j]);}printf("\n");}}static void transpose(int array[3][3]) {int length = 3;int array_copy[3][3];static void copyArray(int dest_array[][3], int origin_array[][3], int length);copyArray(array_copy, array, length);for (int i = 0; i < length; i) {for (int j = 0; j < length; j){array[i][j] = array_copy[j][i];}}}static void copyArray(int dest_array[][3], int origin_array[][3], int length) {for (int i = 0; i < length; i) {for (int j = 0; j < length; j){dest_array[i][j] = origin_array[i][j];}}}

用递归法将一个整数n转换成字符串。例如输入483,应输出字符串“483”。n的位数不确定,可以是任意位数的整数。

本题明确使用递归函数,函数的输入是一个n位数,输出是个位数(末位数)。算法的核心就是求每一位的数字:

  1. n位数直接对10取余,得到个位上的数字
  2. n位数除以10取整,然后再对10取余,得到十位上的数字
  3. n位数除以100取整,然后再对10取余,得到百位上的数字

总结一下这个算法就是:对于输入的数据算出末位数字并输出,然后把输入数据的末位截掉变成下次调用的输入值,只要当前数据不是1位数,那就继续递归调用下去。

递归调用的条件是至关重要的,这里通过判断对10取余的结果是不是他自身来决定是否一直递归调用下去,代码参考:

static void printLastnumber(long number) {int n = number % 10;if (n != number) {printLastNumber(number / 10);}n = (n < 0 && n != number) ? -n : n;printf("%d", n);}

PS:如果输入值为负数,则判断一下不是取到最后一位就把负数转成正数输出,不判断会把“-123”输出成“-1-2-3”。

总结

函数的定义和设计参考以下几点:

  1. 复用性,在项目里有很多模块,如果模块1中要按照某种算法计算两个数的关联性,模块2中也要用同样的算法计算两个数的关联性,这样我们就可以把计算两个数关联性的代码提取出来作为单独的函数,再去供不同的模块调用。
  2. 可扩展,一个函数根据用户数据做商品推荐,随着用户行为的不断丰富,函数体的推荐算法也不断的优化,在不改变函数的输入和输出的前提下去做功能性的扩展和补充。
  3. 解耦合,一个函数A处理输入的时候又要用函数B来预处理输入,或者经过计算后再用函数C去输出,这里A依赖其他函数B和C,使它们之间耦合性增强,牵一发而动全身,这样的函数设计是不提倡的。
  4. 抽象,在之前的文章也提到过“要把业务问题抽象成数学问题”,这一点在后面做实际的项目中才能有深刻的认知。

在熟练掌握C语言函数的用法和一些特性之后,就可以组织构建模块进行常规的项目开发了,后续笔者会做一些简单的但是模块体系相对完善的项目演练,我们一起通过具体项目的开发练习来把C语言这门工具用起来。

往期文章

一起学《C程序设计》第六课——数组、字符串及实战练习

一起学《C程序设计》第五课——循环控制及实战练习

一起学《C程序设计》第四课——if语句、switch语句及实战练习

一起学《C程序设计》第三课——数据结构、运算符、表达式和语句

一起学《C程序设计》第二课——算法

一起学《C程序设计》第一课——C语言概述和学习前的准备、意识

C程序设计(谭浩强)——第五版和第三版对比

    推荐阅读
  • 枕头下放书吉利吗(枕头下放书吉不吉利)

    人的脊柱正常有4个弯曲,只有保持脊柱的正常曲线,才能使身体健美。过低,流入头部的血液偏多,影响头部血液循环,血管充盈,管壁受压,醒来后会感到头晕、眼皮浮肿。长期枕头过高,使颈椎的某部分受压过大,发生骨质增生,直接压迫颈神经,出现头痛、头晕、颈肩臂及手指发麻,甚至不能抬管举手。对有高血压或心肺疾患的病人,可根据病情适当调整枕头的高度。

  • 香港旅游景点(香港旅游景点推荐)

    首先是太平山顶,是香港首屈一指的旅游热点,置身其中,可见醉人的海港、城市和郊野景色;搭乘山顶缆车登山,更是一次兴奋难忘的体验;,我来为大家讲解一下关于香港旅游景点?跟着小编一起来看一看吧!别错过每晚上演的“幻彩咏香江”,维港两岸的建筑物绽放幻彩灯辉,带来声光震撼;天坛大佛,大屿山坐拥全球最高的户外青铜坐佛,还有宝莲禅寺、保留着水上棚屋的大澳渔村。香港迪士尼乐园也非常推荐。

  • 米的种类(米的种类有哪些)

    我们一起去了解并探讨一下这个问题吧!米的种类米的种类有六个,分别是大米,黑米,糯米,糙米,小米和薏米。以大米为例,大米主要是稻谷经过清理、砻谷、碾米、成品整理等一系列工序后制作而成的成品。稻谷的胚和糊粉层中含有差不多近64%的稻米营养和90%以上的人体所须营养元素,大米是南方人民的主要粮食。

  • 开一间麦当劳怎么样(你的存款够加盟一家麦当劳吗)

    你的存款够加盟一家麦当劳吗加盟麦当劳需要一定的条件若是购买新店,你需要缴纳的首付大约是总成本的40%,若是购买现存店,要花费总成本的25%首付只能是自己的资金,不能是借来的它可以是银行存款、证券、债券、利益分享、商业或房地产,。

  • 筑梦新时代致未来的自己(建设一个新时代)

    10月14日上午,深圳经济特区建立40周年庆祝大会在广东省深圳市隆重举行,中建集团旗下中建科工华南大区总工程师陆建新作为深圳代表之一发言。在绝望中,第四次试验成功了。国内已成建筑并无参照对象。2020年,新冠疫情发生时,陆建新还在江苏老家探望父亲。院区建成后,广东省委副书记、深圳市委书记王伟中视察时,这样感慨。从深圳出发,为中国建筑创新“第二座比萨斜塔要诞生了。”

  • 乐山市侏罗纪探险谷暂停营业公告 乐山侏罗纪公园

    近期有计划前往侏罗纪探险谷的游客请合理安排出行时间。在此期间,亦不能乘坐老爷车往返马庙ー芭蕉沟。走进侏罗纪探险谷,映入眼帘的是遮天蔽日的绿色。数十万棵桫椤树和周围茂密的野生植物,让侏罗纪探险谷成为一个天然氧吧,净化游客的肺。

  • 车辆误闯单行怎么处罚(开车误闯单行线怎么办)

    交通道路出行,交警处理的可能性已经很小,大多数都是电子眼抓拍,而电子眼大都设立在单行线的入口和出口处,抓拍违规也只是在出口处。新手司机上路,遇到这样的情况,都会手忙脚乱的,但是宁可被惩罚,也不要做出更多影响交通安全时的事情。在单行线的道路入口,都会有一个路标指示牌,在开车出行时一定要认真观察道路,注意路边的标识,认真开车,才能保障自己和他人的安全。

  • 是你给我等待的感觉(是你苍白了我的等待)

    我们再也不会像以前那样,以彼此为不可替代;我们再也不会像以前那样,那样用力的爱,直到哭了出来。这个世界上没有什么事是放不下的,你放不下,是因为你还不够痛。我真的喜欢你,闭上眼,以为我能忘记,但流下的眼泪,却没有骗到自己。信任就像一张纸,皱了,即使抚平,也恢复不了原样了!不要去欺骗别人,因为你能骗到的人,都是相信你的人。每个人都是幸福的,只是,你的幸福常常在别人眼里。

  • 世界精神卫生日活动策划方案(世界精神卫生日)

    当天,由湖南省卫生健康委疾病预防控制处主办,湖南省脑科医院、湖南省精神卫生项目办承办的系列主题宣传活动启动。刘光亚指出,心理疾病的发生受到遗传和环境的影响。对此,刘光亚建议,在应激事件发生后,除了自我心理疏导和调节外,外界主动提供心理干预措施,监测心理变化,可以有效预防应激障碍的发生。叶海森强调,家长应注意到,不同年龄段的孩子对家庭的需求不一样。

  • 嘴唇边缘发黑什么原因如何调理(哪些原因造成了嘴唇发黑)

    黑色嘴唇的出现可能是以下这些原因造成的。此外,色素沉淀也是造成嘴唇发黑的重要因素,如果长期抽烟,或者肝脏功能下降,都会造成体内黑色素沉淀增多。解决这一情况,只要将肝脏健康调理正常即可。嘴唇发黑的背后想不到竟然会隐藏这些信息,如果出现这种状况,一定要及时查原因,针对性地进行治疗,以免造成更大的健康危害。