1.函数是什么?
在维基百科中,对函数的定义是:子程序
在计算机科学中,子程序是一个大型程序中的部分代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。
一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
2.C语言的函数的分类:
1.库函数(易错点:库函数不是c语言来提供,c语言标准只是来约定好而已,由编译器的厂商提供实现)
举个例子把~
对于size_t strlen(conse char* str)
c语言标准规定:1.函数的功能——strlen字符串的长度;2.函数名(strlen);3.参数:conse char* str;4.返回类型:size_t
2.自定义函数
2.1库函数:
我们在开发过程中每个程序员都可能用得到,为了支持可移植性和提高程序的效率。所以c语言中的基础库提供了一系列类似的库函数,方便程序员进行软件开发。
那我们怎么来学习库函数呢?这时博主要给大家介绍一个网址:www.cplusplus.com
在这个网址,我们需要大概看懂库函数的定义和使用语法以及返回类型。时间久了就会知道怎么用。让我们举个栗子吧~
在这个表格中,首先看红色字体,那是说明库函数的头文件,接着,看文章中的黑体Copy string,下面的英文意思大概就是将源头的地址拷贝给目的地的地址。然后看strcpy下面绿色部分,就知道把const char*source地址拷贝给char*destination了 ,让我们举个代码例子吧~
#include<string.h>
int main()
{
int arr1[20]={0};
int arr2[]="hello";
strcpy(arr1,arr2);//不需要加指针,因为数组本身就是指针;这行代码意思是把arr2拷贝到arrr1里面去
printf("%s\n",arr1);
return 0;
}
2.2自定义函数:
自定义函数和库函数一样,有函数名,返回值类型和函数参数,不一样的是这些都由我们自己来设计,给程序员一个很大的发挥空间
函数的组成:
返回类型;函数名;参数(不限个数,可以0个也可以无数个);函数体
举个代码例子吧~
题目:写一个函数可以找出两个整数的最大值
返回类型,用void的情况:不需要传参,不需要返回值
在举例子之前,要交给大家一个知识哦~
把一个地址里面的值改变:
#include<stdio.h>
in main()
{
int num=10;//先定义num的值是10;
int *p=#//将num的地址传给*p
*p=20;//这样的话,*p可以改变num里面的值
printf("%d",num);
}
再举个例子吧:写一个函数可以交换两个整形变量的内容
3.函数的参数:
3.1实际参数(实参):
真实传给函数的参数,叫实参;
实参可以是:常量,变量,表达式,函数等
无论实参是何种类型的量,在进行函数的调用时,它们必须有确定的值,方便把这些值传给形参
3.2形式参数(形参):
形式参数是指函数名后括号的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用之后就自动销毁了,因此形式参数只在函数中有效。
4.函数的调用:
4.1传值调用:
函数的形参和实参分别占有不同的内存块,对形参的修改不会影响实参。
4.1传址调用:
1.传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
2.这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
那么写代码时如何判断用传值还是传址调用呢?
1.如果函数内部的值需要改变函数外部的值,比如刚才博主写的上一个题的代码,swap是一个函数,对于swap来说,num1和num2是外部函数,swap想改变num1 num2的值,所以可以用传址调用
2.如果对函数的值不进行改变,比如第一个题的代码,比较出较大者,并没有改变两个数的值,所以可以用传值调用
让我们用博主之前发的习题里,举2个例子吧~
1.写一个函数判断是不是素数
2.在1000~2000判断闰年
5.函数的嵌套调用和链式访问
函数和函数之间可以根据实际的需求进行组合,也就是相互调用
5.1嵌套调用
#include<stdio.h>
void new_line() //new_line函数套用three——line,three——line函数又嵌套new——line
{
printf("hehe");
}
void three_line()
{
int i=0;
for(i=0;i<3;i++)
{
new_line();
}
}
int main()
{
three_line();
return 0;
}
函数可以嵌套调用,但是不可以嵌套定义(如下)
5.2 链式访问
把一个函数的返回值作为另一个函数的参数
比如:printf("%d",strlen("abc"));这个函数就是链式访问,因为是把strlen函数的返回值给了printf做参数、
让我们来一个特别好玩的游戏吧~
大家来猜一猜这个会打印出什么呢?相信大家应该很好奇结果吧~让我们接着往下探索!
这个题的答案是4321,这题的关键在于printf的返回值,我们可以去博主给的网站上搜一下printf的返回值,发现printf打印几个字符就返回几(说明库函数的重要性和博主给的网站都很重要哦~)
好啦~让我们看看这个题吧~,首先第一次printf是打印43,然后43是两个字符,然后就打印出来了432,然后2是一个字符,所以最后答案就是4321,是不是很有趣呢?
让我们接着玩吧~
printf(”%d\n",printf(“%d\n",43);
注意哦\n也是一个字符啊!!!也要算进去啊!!
首先,我们先打印出来43,然后换了行,然后43和\n是三个字符,所以打印出3,然后换行,接着3和\n是两个字符,所以打印2~
相信大家都已经明白了吧~有没有觉得c语言很好玩有趣呢?
6.函数的声明和定义
6.1 函数声明:
1.告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了
2.函数的声明一般出现在函数使用之前,要满足先声明后使用。
3.函数的声明一般要放在头文件中的。
比如以下的例子:
相信看了博主的文章的小伙伴会有这样的疑问吧?博主之前写的代码好像没有过声明,那为什么编译器还可以执行成功呢?
大家可以看一下这个代码,在这个代码过程中,咱们是直接定义函数,然后直接使用(因为声明是一种特殊的定义,咱们在执行这个函数之前先定义,就等同于声明+定义了)
6.2函数定义:
函数的定义是指函数的具体实现,交代函数的功能实现
而且在未来,代码比较多,我们的声明是在.h里面声明,.c文件来实现
相信大家都有疑惑,为什么要把代码拆分成.h .c文件呢?让博主来告诉你们答案吧~
原因是1.多人协作 2.代码保护
让我们来看看本章难点吧~
7.函数递归
7.1什么是递归?
程序调用自身的编程技巧称为递归。
递归的主要思考方式在于:把大事化小
7.2递归的两个必要条件:
1.存在限制条件,当满足这个限制条件的时候,递归不再继续
2.每次递归调用之后越来越接近这个限制条件。
让我们来看看几个栗子吧~
7.2.1练习一:
接受一个整型值,按照顺序打印它的每一位
例如:输入1234,输出1 2 3 4
本题的思路是递归和% /的用法,递归可以理解成递推+回归
输入1234,可以表示成
//gml(1234)可以表示成:
//gml(123)(用1234/10) +4(用1234%10得到)
//gml(123)可以表示成:
//gml(12)(用123/10) +3(用123%10)
//gml(12)可以表示成:
//gml(1) +2;
显示出了递推的作用,然而递归是递推+回归,初学者不明白的话可以画图
红色为递推,蓝色为回归,初学者若不理解的话可以仿照这个画图,这样的话就会清晰明白了
让我们看一下函数为什么可以实现调用和递归吧~
好啦。这个知识学完之后我们再来体会一个代码吧~、
7.2.2
编写函数不允许创建临时变量,求字符串的长度。
在讲这个题之前,让我们举个例子看看什么是临时变量吧~
让我们看看这个代码,size_t是一种类型,是无符号整型的,是为sizeof设计的,其数据打印类型是%zd
让我们回归本题,当我为了求字符串长度,在自定义函数里面创建了一个变量count,这个count就是临时变量
让我们看看这道题的解法吧~跟上一个题一模一样哦~
7.3递归与迭代(循环)二者可以相互转化
让我们来看几道例题吧~
1.求n的阶乘(不考虑溢出)
递归的方法如下
如果考虑溢出的话,可以用迭代(类似于循环)
让我们再看一个例子吧~
求第n个斐波那契数(不考虑溢出)
那这时候就有同学问什么时候用递归什么时候用迭代呢?
答案:如果使用递归很容易想到,写出来的代码没有缺陷,就可以用递归
如果写出的代码有很大的问题,比如:栈溢出,效率低下等问题,那么就要用迭代的方法来解决问题了~
好啦,让我们拓展两个思路吧~
1.汉诺塔问题:
给定三根柱子,记为ABC,其中 A柱子上有 n 个盘子,且上面的盘子一定比下面的盘子小。问:将 A柱上的盘子经由 B柱移动到 C 柱最少需要多少次?
移动时应注意:① 一次只能移动一个盘子
②大的盘子不能压在小盘子上
思路:
现在有abc三个柱子, 把n个盘子拆成n-1个和1个,最底下的是1,其余为n-1,那么,借助b可以把n-1个盘子放到b柱子,把最底下的1先运到c柱子中,然后再把放在b柱子的盘子运到c中
2.青蛙跳台阶的问题(类似于Fib数列的问题)
好啦~本章知识就讲到这里啦~,谢谢大家观看哦~