拳击哥提示:闲余时间记得锻炼身体哦 🤼
目录
5.4、写一个函数,输入一个数n,每调用一次,n的值增加1,调用10次(解析)
1、什么是函数?
- 在数学中我们认为函数就是用来更快解决数学问题的,那么C语言中函数可跟我们的数学函数不一样,无论是从定义还是用法都有极大的不同,但归根结底它们都是用来更方便的去解决生活中的问题的。
- 在C语言中,函数是将一个标识符(函数名)关联到一条复合语句(函数体)的C语言构造。每个 C 程序都从主函数main开始执行,也从它或者调用其他用户自定义函数或库函数终止。我们自定义的函数它负责某项特定的任务,相对于主函数里面其他的代码更具有独立性。一般都有参数返回值。而库函数里面的函数它们能实现更多的封装和细节,通常我们通过引入各个库函数的头文件来实现该函数的功能。
2、函数的分类
1.库函数
2.自定义函数
2.1、库函数
库函数,顾名思义就是C语言库中专门提供的函数,专业名词叫基础库。我们程序员都可以通过调用这些库函数来实现自己的一个意图。什么时候我们会用到呢?我来举两个例子:
1.当我们刚开始学习C语言想要将Hello World打印到黑框框上,这时候我们用到一个printf格式化输出函数,来实现打印。那么printf对应的头文件自然是#include<stdio.h>
编辑
2.当我们想要比较两个字符串大小时候,我们用赋值(==)或大小于(>、<)是比较不出来的,这时候我们用到一个strcmp函数,来实现比较。那么strcmp对应的自然是#include<string.h>
编辑
上面两个函数中第一个函数printf是我们编写程序时最常用的函数之一,第二个函数strcmp它不是那么常用,但是为了实现两个字符串的比较我们可以应用。所以为了我们编程的可移植性和编程的效率提供,C语言基础库中提供了一些库函数,方便我们程序员使用。他们大概分为哪些呢:
- 输入/输出支持函数:intput、output、scanf、printf、getchar、putchar等等
- 字符/字符串操作函数:strlen、strcmp等等
- 数学函数:sqrt、pow等等
- 内存操作函数:memcpy、memmove、memset等等
- 日期和时间工具函数:time等等
- 其他库函数
2.2、库函数的使用方法
我们想要记住所有的库函数是不可行的,因此我们可以参考C语言手册:
注意:以上网站非商业广告纯学习用,大家可放心点击!!!
2.3、自定义函数
🤼函数的组成部分是什么呢,如下图:
编辑
- 语句项里面的类容是各种代码组成的功能
- 返回类型分为有返回值和无返回值
- 自定义函数名尽量采用大驼峰的形式RetType或每个单词之间加下划线is_ret_type
- 自定义函数的参数类型跟主函数类型保持一致
🤼下面程序中,函数的返回类型为void,函数的参数也是void。说明,这个函数无返回任何值。只是调用这个函数。
#include<stdio.h> void Test(void) { printf("Hello World\n"); } int main() { Test(); return 0; }
结果为:Hello World
🤼♀️再看一个程序输入一个数输出这个数加1:
#include<stdio.h> int Test(int x) { x = x + 1; return x; } int main() { int a = 0; scanf("%d", &a); printf("%d\n",Test(a)); return 0; }
输入:5
输出:6
上述程序中,Test的返回类型是int,函数参数也是int。说明函数有返回值并且返回的类型是int,传来的类型也是int。
为什么要有自定义函数?上一个大标题我讲到了,C语言提供了大量的库函数。这些库函数的用法都已经固定了,那么如果我们想要做点新的东西怎么办呢我们可以自定义函数,函数里面来实现我们所想的。比如我要写一个小游戏,小游戏里面得有一个菜单,得有一个实现游戏的程序。那么我们可以自定义一个名为CaiDan()的函数和一个名为Game()的函数,这两个函数里面实现的就是这个小游戏的两个部分。我们可以在主函数main()函数里面调用这两个自定义的函数。
编辑
上面程序中主函数main里面调用了两个自定义函数CaiDan()和Game(),两个函数的类型就是void ,void就是无返回值。()里面什么都不填说明什么都不传过来也就是无传参。
3、函数的参数
3.1、实参
- 真实传给函数的参数
- 实参可以是常量、变量、表达式、函数等
- 形参在调用实参时,它们的类型要保持一致,而且要有确切的值
下面四个程序依次列出了,实参的几个常用类型:
//当实参是常量时候 #include<stdio.h> int get_max(int x,int y) { return(x > y ? x : y); } int main() { printf("%d\n", get_max(5, 3)); return 0; } //当实参是变量时 #include<stdio.h> int get_max(int x,int y) { return(x > y ? x : y); } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); printf("%d\n", get_max(a,b)); return 0; } //当时参是表达式时 #include<stdio.h> int ChuanCan(int x) { return x; } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); printf("%d\n", ChuanCan(a+b)); return 0; } //当实参是函数时 int get_max(int x,int y) { return(x > y ? x : y); } int main() { printf("%d\n", get_max(get_max(5, 3), 10)); return 0; }
上述程序分别是实参为常量、变量、表达式、函数的四种情况,您可以复制程序。打印一下,体验四种不同的情况。
3.2、形参
- 形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效
- 形式的参数,我们可以认为它并不真实存在内存中的参数,就像我们脑中的一个想法一样。我不用它,那么这个想法并不真实,它只是形式的存在我的脑海中。只有我用到了这个想法它才算是真实的。形参也是一样的,你只有调用它,那么它才会在内存中开辟一道空间否则它就单单是几行代码而已。我们这样理解:形式参数是实际参数在内存中的若干份临时拷贝。
4、函数的调用
4.1、传值调用
函数的实参的形参分别占用不同的内存块,我们对形参的修改是不会影响到实参的。
//方法一 #include<stdio.h> int Max(int x, int y) { if (x > y) { return x; } return y; } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); int max=Max(a, b); printf("%d", max); return 0; } ---------------------------- //方法二 #include<stdio.h> int Max(int x, int y) { return (x > y ? x : y); } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); printf("%d", Max(a, b)); return 0; }
以上两个程序的功能是一样的
输入:10 9 输出:10
输入:9 10 输出:10
🤼第一个程序图解:
编辑
🤼♀️第二个程序图解:
编辑
那么上面两个程序中,我们自定义了一个名为Max的函数,我们通过主函数来调用它的功能,实现了比较两数的大小然后返回一个最大值给主函数。
4.2、传址调用
- 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
- 这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量
🤼有一程序,用函数实现输入两个数并交换这两个数:
#include<stdio.h> void Swap(int x, int y) { int tmp = x; x = y; y = tmp; } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); printf("交换前:a=%d b=%d\n", a, b); Swap(a, b); printf("交换后:a=%d b=%d\n", a, b); return 0; }
输入:5 6
输出:交换前:a=5,b=6
交换后:a=5,b=6
我们可以看到,它们并没有交换彼此。为什么会这样呢,那是因为Swap函数里面交换的仅仅是形参里面的x和y的值,也就是实参的临时拷贝的那两块地址里面的值,而实参里的a和b地址里面的值并未发生改变还是原来的值,所以传回的值还是5和6。我们可以借助下图理解:
编辑
我们可以这样改:
#include<stdio.h> void Swap(int* x, int* y) { int tmp = *x; *x = *y; *y = tmp; } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); printf("交换前:a=%d b=%d\n", a, b); Swap(&a, &b); printf("交换后:a=%d b=%d\n", a, b); return 0; }
输入:5 6
输出:交换前:a=5,b=6
交换后:a=6,b=5
上面说到了,形参只是实参的一块临时拷贝。那么如果我把实参的地址给形参了,那么你临时拷贝的地址是不是就与实参的地址一致了,那交换的值也就一致了。
编辑
那么上述程序范围类型是void也就是无返回值,它们只是修改了内存中值。
4.3、代码的调试
下面一个截图我们可以很清楚的观察到,实参传值给形参后它们在内存中存放的地址是不同的。编辑
下面截图中,实参把地址传给形参后,它们的地址是一致的,所以最后实现了交换。
编辑
那么如何监视程序呢,下面我就来介绍:
第一步:按F10打开调试当程序左方出现箭头证明可以开始调试
编辑
第二步:打开调试窗口,任选一个监视。
编辑
第三步:按F10代码会往下走,你可以在监视窗口输入你想监视的内容。注意:监视窗口里面的内容是要输入的,你输入a则输出a的值,你输入&a则输出a的地址。
编辑
第四步:当你箭头指向scanf时你按F10然后输入a和b的值,a和b的值会自动变成你所输入的值。
编辑
第五步:当箭头来到Swap函数的时候按F11,自动跳转到函数里面
编辑
第六步:分别在监视窗口输入x,y,tmp,以便观察它们的变化
编辑
正在发生变化
编辑
最后一步停止调试
编辑
以上就是监视窗口的六个步骤,耐下心来一步步实现,相信你能成为一个调试高手!!!
5、题目练习
5.1、写一个函数判断一个数是不是素数(解析)
#include<stdio.h> #include<math.h> int PanDuan(int n) { for (int i = 2; i <= sqrt(n); i++) { if (n % i == 0) { return 0; } } return 1; } int main() { int n = 0; scanf("%d", &n); if (PanDuan(n)) { printf("这是一个素数\n"); } else { printf("这不是一个素数\n"); } return 0; }
输入:9输出:这是一个素数
解析:
素数满足条件是除1和本身之外都没有任何因子被它整除,上述程序中主函数传参到形参里面PanDan函数里面主要做的是从2开始直到n的开方数依次做为除数用传过去的参数,n依次从2除到n的开方数。如果能整除说明该数不是素数返回0,如果不整除则返回1。返回的值由if判断语句来执行,判断为真输出这是一个素数,否则这不是一个素质。
5.2、写一个函数判断一个数是不是闰年(解析)
#include<stdio.h> int LeapYear(int n) { if (n % 400 == 0 || n % 4 == 0 && n % 100 != 0) { return 1; } return 0; } int main() { int n = 0; scanf("%d", &n); if (LeapYear(n)) { printf("这是一个闰年\n"); } else { printf("这不是一个闰年\n"); } return 0; }
输入:2000输出:这是一个闰年
解析:
首先闰年的满足条件为:能被400整除或能被4整除但不能被100整除,这个程序跟第一个程序类似,都是传一个参数过去传一个参数回来,那么LeapYear里面的功能主要是判断n是否为闰年如果是返回1,否则返回0。返回的值再交给if语句来判断满足1则输出这是一个闰年否则这不是一个闰年。
5.3、写一个函数,实现一个有序数组的二分查找(解析)
#include<stdio.h> int Find(int arr2[],int key2,int str) { int left = 0; int right = str-1; int mid = 0; while (left<=right) { mid = (left + right) / 2; if (arr2[mid] > key2) { right = mid - 1; } else if (arr2[mid] < key2) { left = mid + 1; } else { return mid; } } return -1; } int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; int key1 = 0; scanf("%d", &key1); int str1 = sizeof(arr1); int rat = Find(arr1, key1,str1); if (rat==-1) { printf("没有这个数\n"); } else { printf("有这个数,下标为%d\n", rat); } return 0; }
解析:
首先我们主函数要初始化一个有序数组,然后输入一个数,求出这个数组的元素个数,分别把数组,输入的数,数组元素个数传给形参。Find拿到这三个值后。设置一个左下标left为最左的下标0,设置一个右下标right为最右的下标数组元素的个数-1,和一个中间下标mid。然后进入循环,循环里就是进行查找。
第一种情况:输入的数比中间那个数大,假设我输入的key1为8,传给key2。下图为初始状态
编辑
第一步、arr2[mid]<key2,就使left=mid+1,此时left=5,right=9,mid=7。
编辑
第二步、 arr2[mid]=8,返回mid给主函数并结束Find函数里面的程序,传回mid值给主函数。然后输出:有这个数,下标为:7。
第二种情况,输入的数比中间的数小,假设我输入的数key1是3传给key2,下图为初始状态
编辑
第一步、 arr2[mid]>key2,就使right=mid-1,此时left=0,mid=1,right=3。
编辑
第二步、arr2[mid] <key2,就使left=mid+1,此时left=2,mid=2,right=3。
编辑
第三步、arr2[mid]=3,返回mid给主函数并结束Find函数里面的程序,传回mid值给主函数。然后输出:有这个数,下标为:2。
5.4、写一个函数,输入一个数n,每调用一次,n的值增加1,调用10次(解析)
#include<stdio.h> int BigOne(n) { return (n + 1); } int main() { int n = 0; scanf("%d", &n); for (int i = 0; i < 10; i++) { n=BigOne(n); printf("%d ", n); } return 0; }
输入:5输出:6 7 8 9 10 11 12 13 14 15
解析:
这个函数就比较简单,输入一个数,把这个数传给形参,主函数里面for语句控制传参十次,BigOne函数里面每次传参过来,返回参值加1 。然后输出每次一传回来的参值。
5.5、实现猜数字小程序(随机生成1-100内的数字)
#include<stdio.h> #include<stdlib.h>//rand()函数、cls清理屏幕函数都用此头文件 #include<time.h>//time时间戳 void caidan()//这是一个菜单 { printf("*********************\n"); printf("***** 开始:1 *****\n"); printf("***** 退出:0 *****\n"); printf("*********************\n"); } void game()//这是游戏函数 { int rat = rand()%100+1; int num = 0; while (1) { printf("请输入你的数字:>"); scanf("%d", &num); if (rat < num) { printf("你猜大了\n"); } else if (rat >num) { printf("你猜小了\n"); } else { printf("恭喜你,你猜对了\n"); break; } } system("cls"); } int main() { int n = 0; srand((unsigned)time(NULL)); do { caidan(); printf("请选择:>"); scanf("%d", &n); switch (n) { case 0:printf("你已退出游戏\n"); break; case 1:game(); break; default:printf("请输入正确的数字\n");break; } } while (n); return 0; }
那么本期博客就到这里了,感谢大家的耐心观看,如有收获请三连,万分感谢!!!
编辑
每次学习都要像打拳一样全神贯注,这样才不会被对手(困难)击倒