前言

C语言编程又称模块化设计,讲的就是对于一个程序而言,每一个功能都要类似于独立的实现,就像一个个板块,需要的时候拿出来即可。模块化的设计思想是一个程序员必不可缺少的思想。

如果有不明白的地方,或者想讨论的题目,欢迎大家找我     QQ:482999194  

这是我的gitee地址,每日更新一些有趣的题目等:濡白记录一些有趣题目的小仓库

目录

函数类型​

main主函数

库函数​

自创函数​

函数使用​

函数类型与函数的返回值​

形参与实参​

传址调用与传值调用

函数设计要求​

高内聚低耦合​

好的函数名​


函数类型

说到模块化设计,就不得不提到函数。什么叫函数,就像高中数学中提到的,函数的定义就是映射,对于C语言而言,函数的意思就是,一系列固定的操作,或者说过程。

main主函数

main主函数是一个程序的入口,也就是说,所有的程序将从主函数进入,然后顺序结构执行。对于任何一个项目,只能有一个主函数,这也就是很多同学初学C语言的时候遇到的一个bug,往往写完一个程序之后接着打开了另一个源文件创建了一个新的main主函数,又不把原先的主函数删除或者注释掉,所以也就运行不了啦!

库函数

顾名思义,库函数就是库里面的函数。什么叫库呢?就是已经存在的一个仓库一样的东西。像printf这类函数,很多人都会用到,那么为了防止每个人的printf函数不一样,导致你的程序没法在我的电脑上运行,C语言本身附带了一些仓库,这些仓库就储存了一些平时经常遇到的函数,这些就是库函数。

而想要从库中用函数,首先我们就需要让我们的程序包含这些库,这也就是我们平时写程序基本上都要带上这样一句话:

#include<stdio.h>

这个语句的意思就是告诉系统,我的这个程序包含了stdio.h这个仓库,由此我们才能用这个仓库里的库函数。同样的,对于一些我们不需要用到的库,我们自然也就不需要引用了,就像你去上数学课,背上一本近代史课本当时是不必要的,不仅不用,而且背在身上也是一种负担。

自创函数

但是光有库函数是不够的,比如存在一个库函数是将以一个数字×2,那么如果你是想要×3+2呢?又或者是×65+52呢?又或是......所以说,库函数往往不能满足我们写程序时的一些个性化需求,这个时候就需要我们自己创建我们自己需要的函数了。而这类函数就被我们称之为自创函数。

函数使用

函数类型与函数的返回值

说到函数,大家最先可能产生疑惑的就是为什么是int main(),而不是其他的float main之类的,还有为什么所有的main函数最后都需要加上一句return 0,为什么不是return 1 呢?这就要说到函数类型与函数的返回值了。

就像之前说的,函数就是一个映射,通过一系列操作得到某个值,例如给定一个字母,得到下一个字母,那么结果就是字母类型,如果给定一个数得到他的两倍,结果就是数字类型。同样的C语言中的函数也是一样的,函数类型就是告诉计算机最后得到的会是什么类型的结果,而函数的返回值也是一样的道理,已经知道了最后的结果是字母类型,就肯定不能得到一个数字类型。也就是说 函数的返回值是在声明函数的时候就已经确定好的

函数的声明与定义 

我们已经知道函数的返回值是在函数声明的时候确定好的,那么也许同学们经常会听到两个不同的词——函数的声明与函数的定义。

这两个乍一听非常的接近,但是实际上还是有些许的不同。

首先对于函数的声明,就是告诉计算机,存在这样一个函数,这样计算机才愿意继续运行下去。这就像数学老师让你做一道很难的题,如果超出你的知识水平你当然不愿意尝试,但是如果老师提前告诉你只需要你把一个数带入她告诉你的一个公式计算就好了,这个时候你当然愿意去尝试一下。那么函数的声明就是这样的一个过程。

void function(int num);

这就是函数的声明,只需要短短的一句话就可以了,需要注意的是这句话一定要放在主函数的前面哦,就像老师一定要在你去参加比赛之前告诉你一样。还有一点,就是函数的声明最后一定要跟上一个分号 “;”,因为声明就是一句话的事情,那么C语言中一句话,就肯定对应着一个分号。还有非常非常重要的一点就是,在函数的定义之中是不能进行函数的声明的

接下来让我们看一下函数的定义,类比刚才提到的例子,老师已经告诉你只需要带入一个式子计算机就可以了。那么函数的定义,就相当于老师告诉你那个式子是什么,只有知道那个式子才能解决那道问题不是吗?

不过值得区别的是,函数的定义可以放在源项目的任何一个地方除了函数的声明之前。但是在此说一个特殊的情况,就是项目没有函数的声明,直接就是函数的定义,这个时候来说,函数的定义同时也充当了函数的声明的作用,不过此时函数的定义也需要放在主函数之前。但是并不推荐这种写法,原因很简单,就是当一个项目中的函数多了起来之后,如果都采用这种方法,主函数就势必跑到了最后,这样的写法emmm......可以说是非常的丑,使得你自己代码的可读性非常的低。而如果采用增加函数声明的办法,那么阅读者可以非常直接的看到你采用了哪些函数,同时也可以非常快速的浏览到你的主函数。

形参与实参

让我们看一下这个代码:

#include<stdio.h>int func(int num);int main()
{int a = 0;a = func(a);printf("%d", a);return 0;
}int func(int num)
{return num + 1;
}

函数func后面的括号,就是函数的参数,表示这个函数需要用到哪些变量来进行映射。

那么看一下main函数中func被调用的时候传入的就是int 类型的a,在被调用的函数之中,类似于a这样的就被称作实参,表示这个是实际意义上的参数。而进入到函数之中就会产生一个和a几乎一样的变量,也就是函数定义中的int num,这个num就是所说的形参,这个变量是进入函数的时候创建,一旦出函数就会被销毁,也就是说main函数之中无法调用num,因为func函数一出来就不存在num这样的一个变量了。而更多的细节,接下来让我们以函数的传值与传址来讲解。

传址调用与传值调用

首先在此阐述一下我个人的观点,所谓传值和传址,本质上都是传值,真正做到改变实参值的操作是”解应用“。刚才已经说到,函数的形参就是进入函数的时候创建,出函数的时候销毁,那么如果我们在函数中对变量进行修改自然而然也就无法影响到实参,如下:

#include<stdio.h>void func(int num);int main()
{int a = 0;func(a);printf("%d", a);return 0;
}void func(int num)
{num = 1;
}

打印的结果是0,正好验证了我们的思路,而这种操作就是我们通常意义上的”传值调用“

而传址调用,意思就是传地址调用。代码如下:

#include<stdio.h>void func(int* num);int main()
{int a = 0;func(&a);printf("%d", a);return 0;
}void func(int* num)
{*num = 1;
}

这里我们注意到此时的形参变成了int* num,这里的变量是一个int类型的指针,而在此大家理解指针就是地址即可,主函数中的传参自然也就需要变成一个地址,&就是一个取地址符号。&a就是表示a在内存中存放的地址。

再回到func函数中,我们可以看到num前面加上了一个*,这个*的意思就是解应用,也就是通过地址找到main函数中的a,这个时候对其进行修改,那么main函数中的a的值自然而然也就会发生更改。这种也就是我们通常意义上的”传址调用“。

但是为什么作者会认为传址本质上还是一种传值呢?让我们先来解决一道简单的交换变量的题目:

给出两个数字,调用函数对其的值进行交换。

#include<stdio.h>void exchange1(int*, int*);
void exchange2(int*, int*);
void exchange3(int*, int*);int main()
{int a = 1, b = 2;exchange1(&a, &b);printf("%d %d", a, b);exchange2(&a, &b);printf("%d %d", a, b);exchange3(&a, &b);printf("%d %d", a, b);return 0;
}void exchange1(int* a, int* b)
{int* t;*t = *a;*a = *b;*b = *t;
}void exchange2(int* a, int* b)
{int t = *a;*a = *b;*b = t;
}void exchange3(int* a, int* b)
{int *t = a;*a = *b;*b = *t;
}

上面给出了三种非常经典的写法,其中只有exchange2是正确的,下面让作者逐一进行分析。

exchange1

        首先,这个函数可能在部分编译器上是无法运行的,比如我正在使用的vs2022,系统会给出一个报错叫int* t 未初始化。而在有些编译器上也许是可以运行的,而且有的同学会惊奇地发现,好像运行出来的结果还是正确的,那为什么我坚持说这是一种错误的写法呢?答案就是系统给出的这个报错,也许有的同学会知道这个就叫做野指针,野指针所指向的地址是不确定的,也就是说他可能恰好指向一块不可以被修改的空间,例如在之前的bolg中说到的常量,常量是不可以被更改的,那么系统此时就会出现运行错误了,故这种方法是非常不可取的。

exchange2

对于第二个正确的函数简单分析,就是我们平时用的三变量交换法,唯一的区别就在于,此处对于a,b需要我们采用解应用的方式找到main函数中对应的实际上的a,b才能实现变量的交换。故在此不做过多解释,如果不理解的欢迎大家私信我。

exchange3

        其实最多人会犯的错误就是这一种,乍一看好像是没有什么错误的,但是实际上的运行结果确实没有实现交换,为什么呢?其实让我们再次理解一下函数传参的过程,函数传入一个参数,然后函数会创建一个几乎一模一样的参数,然后对于这个新创建的函数进行操作。到此我们可以这样理解,就是指针num是一个变量,这个变量存放的是a的地址,当我们对这个地址进行操作的时候,我们实际上改变的是num存放的地址,但是实质上我们并没有改变a的地址,而如果想要改变a的地址,那么我们就需要用到二级指针进行操作,再次不做过多阐述。

既然这样,那么我们就可以推出,传址调用实质上也是传值,只不过传的值存放了一个地址而已,如果有会调试技巧的同学也可以采用单步调试的方式,观察一下num的地址,相信这样能更深入地理解我的意思。

函数设计要求

高内聚低耦合

高内聚低耦合的意思就是,函数之间的关联要尽可能的小,也就是一个函数往往只完成特定的某一个步骤,这样的函数才是一个好的函数。比如一个打印1000-2000之间所有的闰年的函数,那么对于这个函数,它所需要的应该就只有判断是不是闰年,而函数内则不需要打印,判断完之后则有主函数决定是否打印。

那么这样的好处是什么呢?举个例子就是,假如我们还需要计算某一年有多少天,那么我们此时也需要对这一个年进行一个判断,判断他是不是闰年,但是此时我们仅仅需要判断是不是闰年即可,并不需要对其进行打印,那如果之前那个函数增加了打印,对于这个问题就无法解决,也就意味着我们还需要另一个函数来实现我们的功能,无形之中也就增加了无效的代码。所以函数设计时,做到高内聚低耦合是非常有必要的一个好习惯。

好的函数名

一个好的函数名可以增加代码的可读性,也就是当别人看到你的函数声明的时候就可以很快理解你这个函数大概是要做一件怎么样的事情。一个非常有名的笑话就是”冒泡排序“,很多同学的命名就是void maopao,更有甚者void gulugulu表示冒泡的声音。

【濡白的C语言】初学者-从零开始-5(模块化设计——函数,传值和传址)相关推荐

  1. 【濡白的C语言】从零开始的刷题之旅

    前言 每个人都是从小白成长起来的,在此篇记录一些我从小白开始刷到的一些有意义的题目,不定期更新题目,建议点赞+收藏后享用~感谢支持!,也欢迎评论出想看的题目类型,会考虑添加的! 题目的话我会给出题干, ...

  2. 【濡白的C语言】初学者需要注意的坑,亲身经验

    前言 大部分学习计算机的人首先接触的一门语言都是C语言,但是经常会发现对于同样的课程,学习完之后不同的人能力却有着天壤之别 !而差别出现的原因大部分是学习过程的方法不好,于是精心整理一下了自己学习C语 ...

  3. 【濡白的C语言】初学者-从零开始-1(printf与变量)

    目录 HELLO WORLD 主函数main printf 简单加法 变量 不同变量的大小 sizeof 再提printf 简单输入并加法 scanf 记得三连哦~ ​ HELLO WORLD //简 ...

  4. 【濡白的C语言】初学者-从零开始-4(欢喜冤家static与extern)

    前言 本次主要介绍两个关键字static.extern,主要与函数.变量的作用域与生命周期有关. 目录 extern C语言函数隐式存储方式 全局变量 函数 static 局部变量 全局变量 函数 总 ...

  5. 【濡白的C语言】初学者-从零开始-2(常量,一维数组,转义字符)

    目录 常量 字面常量 常变量 标识符常量 枚举常量 数组 字符串数组 字符串结束标志\0 strlen函数 不完全初始化 转义字符 常量 常量,当然与变量想法,变量可以赋值改变,那么常量就是无法改变的 ...

  6. 【濡白的C语言】初学者-从零开始-3(三大结构—顺序结构,分支结构,循环结构)

    前言 学习C语言,必须要掌握的是三大结构--顺序.分支和循环.一切C语言程序都可以用这三个结构总结,因此熟练掌握它们,对于写程序以及理解程序都是非常有必要的. 目录 顺序结构 变量声明 函数声明 循环 ...

  7. 【濡白的C语言】数据的存储(大小端模式,原码反码补码,浮点数的存储,浮点型精度缺失的原因)

    前言 很多学习C语言之后就会对各种类型感到很烦,但是数据的类型具有相当的意义.首先是类型决定了大小,即该数据在内存中开辟的空间大小:同时不同的类型还决定了数据存储的方式,相同的数据,存入整形与浮点型方 ...

  8. C语言中函数调用中的传值与传址

    首先介绍一下函数中传值与传址的概念: 传值:传值,实际是把实参的值赋值给行参,相当于copy.那么对行参的修改,不会影响实参的值 . 传址: 实际是传值的一种特殊方式,只是他传递的是地址,不是普通的赋 ...

  9. 【c语言】带你快速理解函数的传值和传址

    创作不易,本篇文章如果帮助到了你,还请点赞支持一下♡>

最新文章

  1. gunicorn源码分析
  2. machine learning (2)-linear regression with one variable
  3. 63个优秀测试网站地址【个人总结】
  4. google账号解除游戏绑定_附方法!关于物联卡手机号的绑定与解绑
  5. 第14篇:Flowable-BPMN操作流程之任务完成
  6. 2017.8.17实操总结
  7. c语言基础程序设计报告,c语言程序设计基础课程设计报告.doc
  8. 上网课的心得体会1000字_网络学习心得体会 (1000字)
  9. tcm_fc自动加载 linux,[Fibre Channle 实战之四]centos7中如何添加tcm_qla2xxx支持
  10. gateway配置mysql_gateway中这样使用mysql正确么
  11. 罗永浩关联直播交易案遭“问停”;中国量子计算原型机“九章”问世;pip 20.3 发布 | 极客头条...
  12. jQuery再学习之二、jQuery选择器
  13. linux命令行下如何播放视频,linux播放视频命令
  14. python 前端开发_python和前端开发怎么抉择?
  15. 安卓平板隐藏虚拟按键_实现安卓设备虚拟按键隐藏和显示的方法和系统的制作方法...
  16. 兔将十年大作《赤狐书生》特效解析:青蛙精篇
  17. 把Redis当作队列来用,真的合适吗?(转载)
  18. 旋转编码器详解(主要讨论增量式编码器与绝对式编码器)
  19. 莱布尼兹天才发现,改变人类发展轨迹
  20. Vimeo高级API:使用SCRIBE在JAVA中搜索公共视频

热门文章

  1. 如何减小电压跟随器输出电阻_如何测量电压?
  2. 整型转字符串itoa
  3. html编写中h1标题行左,英语编写台词 颜色 表演
  4. 雷电模拟器Xposed 安装
  5. 马云鹤:人间四月,芳菲深处是流年
  6. 汽车中控介绍(笔记)
  7. C++课程设计实训报告
  8. JavaScript高级(详细总结)
  9. 若依mybatis升级为mybatis-plus
  10. ubuntu源更新问题的坑