今天,我来分享一下我对函数的见解,前两次的博客代码太少,这次代码会很多,注意哦!
说到函数,大家一定很熟悉,数学里的函数想必大家并不陌生,而计算机语言的函数和数学函数一样各式各样的。C语言函数是由返回值,函数名,函数参数,函数体构成。函数的参数传参是从右往左,函数调用后需要将占用的内存释放,这两点很重要。C语言的函数又分为自定义函数和库函数,下面我将一一讲解。
自定义函数
说到自定义函数,相信大家一定听过先声明,后调用的经典吧。其实我们的main函数也是我们自定义的一个函数。下面我先介绍一下main函数,main函数是程序的入口,并且有且只能有一个。
这三种定义主函数的形式相信大家或多都见过,但main函数实际上是可以有参数的,
int main(int argc,char*argv[]) {
return 0;
}这个形式的函数就是有参数的main函数,但main函数的参数是规定的,第一个参数argc记录了输入控制台的字符串个数,而argv是字符指针数组,存放了每个字符串的首地址,并且长度是根据输入的个数来规定的,有参数的main函数的使用环境是在 *** 作系统下进行的, *** 作系统方面的内容,我接触很少,这里就不分享 *** 作了,感兴趣的小伙伴们可以自行查阅资料。
自定义函数是我们为了实现某些功能而创建的,当有很多重复复杂的 *** 作时,可以使用自定义函数来进行优化。理论上来说,只要是能运行的正常主函数,都可以用自定义的调用函数来实现。出开始学习C语言开始,设计了很多简单的程序,但一开始都是为了解决特定问题而设计的程序,缺少普遍规律,在学习了自定义函数之后,我希望大家喜欢上设计调用函数来解决普遍问题。随着C语言的发展,我们也有了很多设计好的并且可以引用头文件后直接使用的自定义函数,这些函数被称为库函数,下文会介绍一些常见的库函数。
在使用自定义函数之前,我们需要先进行声明。那什么是函数的声明呢?简单来说,就是告诉编译器有一个函数,它叫什么,参数是什么,返回值是什么类型的,但是这个函数是否存在,这个函数是否有具体内容并不重要。“先声明,后调用”希望大家一定要铭记。在使用一个库函数之前我们往往需要引用一些头文件,这些头文件里就包含了函数的声明,在做一个大项目的时候,自己写自己的main函数显然不合适,这个时候我们需要将函数声明放在一个自定义的头文件中。
这就是创建头文件的一种格式,至于为什么会需要这样以后再做探讨。
函数的定义是指函数的具体实现,交代了函数的功能,我一般是定义函数和函数声明一起进行。
图1是我比较喜欢的形式,图二中的int Add(int x,int y);也被称为预声明语句,里面的x和y是可以省略的,至于为什么将函数主体定义在main函数之后需要使用预声明语句,你需要了解编译的过程。
接下来是函数的参数,函数参数分为实际参数和形式参数。
实际参数
传给函数的参数,实参可以是表达式,常量,甚至可以是函数等等,但无论是怎样的形式,实参都必须有真实的值传给形参。
形式参数
值的是函数名括号里中的变量,如前面两个图中的x和y,形式参数只有在函数调用的时候才会向内存访问一块空间,并且在调用结束后,这块空间会还给内存,所以形式参数只在函数内部有效。举个例子,就是一个在外漂泊流浪想要回到故乡的人,只有在遇到村庄的时候才能找个地方住,其他时候都是风餐露宿,但他想要回到故乡,所以在待了一段时间后终究会离开村庄。在外漂泊流浪的人就是形式参数,路上的村庄就是内存,村民们给他的住处就是访问到的空间,而离开了村庄就代表调用结束,他要继续漂泊流浪寻找故乡了。
接下来看一段
根据结果我们可以发现,swap1没有实现交换目的,但swap2实现了交换的目的。
这就是传值调用和传址调用的区别,swap1为典型的传值调用,传值调用是,形参只是实参的临时拷贝,形参的改变并不会影响实参。而swap2为传址调用,将a和b的地址传给实参,形参指向的地方是实参,这时使用形参可以对实参进行改变。
函数的嵌套调用和链式访问
函数的嵌套使用,指一个函数调用了另一个函数,在main函数中使用了printf函数就是一种函数的嵌套使用哦。函数的嵌套使用也有两种比较难的情况,回调函数和递归函数,递归函数在下文会进行介绍,回调函数现在介绍还为之过早。
函数的链式访问,指把一个函数的返回值作为另一个函数的参数。
上面的代码就是典型的实例,printf函数也有返回值,它的返回值其实是printf函数“”中的字符串长度,当然,不包括。
函数递归
递归是程序设计语言中的一种算法,被广泛应用。递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,通常是将一个复杂的大型程序层层转化为一个规模较小的问题,用少量的程序完成了多次的重复计算,大大减少了代码量。这其实和我们高中学过的递推数列的通项公式很类似。下面举两个例子
我们都知道,在数学中,乘法其实是进行了多次同样的加法,次方其实是进行了多次同样的乘法,C语言中的^是按位异或,并不是次方,而我们想要直接求a的b次方需要引用
1.存在限制条件,当满足限制条件时,递归调用停止。
2.每次递归调用之后都会越来越接近这个限制条件。
如果没有这两个条件,递归会出现栈溢出的情况即stack overflow。由于编译器原因,这里不给大家分享栈溢出的代码了。我们继续讲递归,递归虽然使代码量减少,但是程序运行量并没有减少,甚至可能比正常的程序代码量还要更多。大家应该都知道斐波那契数列吧,接下来以这个为例。
虽然第二个代码比第一个代码简洁,但是递归实现的斐波那契数列分支太多,当n很大时计算量是巨大的,而第一个代码则没有那么多的计算量,所以递归使用也需要慎重考虑,理论上,循环都有可以用递归实现,但实现递归可能会出现上面代码二的情况哦。
大家如果要学习递归调用可以从简单的求阶乘,汉诺塔和小青蛙跳台阶问题来解决,甚至可以尝试一下解决牛顿迭代方程。
最后提一句,掌握自定义函数的核心其实是了解算法,希望大家可以自定义函数多设计一些各种各样的程序。
库函数
库函数是之前的程序员设计好的函数,放进库里,供其他人使用的函数。
库函数这里只对一些重要的库函数进行介绍,不做过多的讲解,以后会详细讲解一些常见的库函数。
首先是IO函数,I代表input输入,O代表output输出,即输入输出流函数。一般头文件为stdio.h
printf函数是我们最常见的IO函数之一,他的作用是格式化打印“”中的字符,格式化打印符会自动寻找变量或者常量来进行替换,格式化打印符第一篇有详细的介绍,如果有遗漏可以在评论区留言进行补充。而printf的返回值就是字符的位数,不包括。
scanf函数也是比较常见的函数,比较容易犯在“”中加入非格式化转换符和忘了取地址的错误。
getchar是接受键盘上的任意一个字符(getchar还可以与文件联系到一起),putchar是打印一个字符。
gets输入一个字符串,puts打印一个字符串,值得一提的是gets在c++中已经被弃用,这也是我最近在知道的东西。
接下来是字符串 *** 作函数,就是对字符串进行 *** 作的函数。头文件一般是string.h
strcpy字符串拷贝函数,基本使用方式是strcpy(string1,string2);将字符串2拷贝给字符串1;
strlen求字符串长度函数,遇到就停止计数,基本用法是strlen(string);我们总会吧strlen和关键字sizeof联系到一起,当介绍数组时,我会尽力去解释这两个的区别。
strcmp字符串比较函数,将两个字符串进行比较,基本使用时strcmp(str1,str2);当str1大于str2时返回大于零的数,等于时返回0,小于则返回小于0的数,是根据ASCII码值进行比较的。
strcat字符串追加函数,strcat(str1,str2)是将str2字符串接在字符串str1的后面
strupr大写字符串函数,使用方法是strupr(string);会将string中的所有小写字母变成大写字母。
strlwr则是小写字符串函数,使用方法与strupr一致,但是是将所有的大写转换成小写。
字符 *** 作函数,是对字符直接 *** 作的函数,接触很少,没有具体的例子,希望大家来补充。
内存 *** 作函数,是对内存进行 *** 作的函数
我们来简单介绍一下其中的memset,内存设置函数,头文件为string
memset(指针或者数组a,赋给a的值,a的长度)例如(a,,sizeof(a))代表情况数组a
接下来是时间/日期函数,头文件是time.h对时间进行 *** 作的函数,这里也没有例子
然后是数学函数,顾名思义是数学的函数,头文件是math.h
sqrt求根函数,pow是求幂的函数,abs函数求绝对值等等,大家感兴趣可以去了解一下。
值得一提的是这些函数的返回值都是double型的
最后是其他库函数,这里介绍一个直接对 *** 作系统下达命令的system函数
system系统函数,头文件是stdlib.h,system(“”)中的“”下达的命令和在cmd中下达的命令是一致的,懂得电脑的大概已经迫不及待的想要使用system函数了吧,不懂电脑的没关系,我也是一个电脑小白,我们总会慢慢学会的。
C语言初阶的函数讲解到这里就介绍了,感谢大家的阅读。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)