前言
“总有人间一两风,
填我十万八千梦”
🍑作者:小赛毛
💕文章初次日期:2022/7/24
目录
1.函数是什么?
一说到函数很多小伙伴呢,就会联想到数学中我们常见到的函数概念,但是程序中的函数是什么呢?
维基百科中对函数的定义:子程序
2.C语言中函数的分类
在我们现阶段学习的C语言之中,函数分为:
- 库函数
- 自定义函数
2.1库函数
日常中,我们常见的printf、scanf、strlen等都是库函数,这时候就会有小伙伴发问了,为什么要有库函数呢?其实在早期的C语言当中是没有库函数的,这是因为没有了库函数就会造成针对某一功能的实现可能不同的人自命名了多种不同的名字、使用了多种实现方法,这就造成了:
- 代码冗余
- n开发效率低
- 不标准
这个时候呢,把常用的一些功能实现成函数,集成为库,由C语言直接提供~然后C语言标准,就可以规定库函数的标准,这种做法呢方便了程序员进行软件开发。
那怎么学习库函数呢?
这里我们简单的看看:https://cplusplus.com/
2.2自定义函数
有很多小伙伴呢,这个时候又要发出疑问了,为什么有了库函数,还要有自定义函数呢?
这里有一句话说的非常好:如果库函数能干所有的事情,那还要程序员干什么?
所以我们更加看中的是自定义函数
自定义函数和库函数一样,有函数名,返回类型和函数参数。
但是不一样的是这些都是由我们自己来设计的,这就给了程序员很大的发挥空间
这里呢我们举个栗子🌰:
//写一个函数可以找出两个整数中的最大值
//写一个函数可以找出两个整数中的最大值 get_max(int x, int y) { x > y ? x : y; } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); //使用函数求a和b的较大值 //Max; int m = get_max(a,b); printf("%d", m); return 0; }
//写一个函数可以交换两个整形
首先我们先来想一下如何实现这个函数:
我们先来写这样一段代码:
//写一个函数可以交换两个整形 //形参 void swap(int x, int y) { int tmp = 0; 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; }
CTRL+f5 代码走起来,我们发现结果是
显然我们我们的程序出现了bug,那么是哪里出了问题呢?
这里我们就可以利用调试和监视来一看究竟:
继续摁下f10 输入两个值
经过分析,我们可以看出x与y有了独立的空间交换的是x与y的值,a和b的值并没有发生改变
这里呢,我们应该知道a、b叫做实参,x、y叫做形参,我们也得出一个结论:
当实参传给形参的时候,形参是实参的一份临时拷贝,对形参的修改不会影响实参。
当我们了解到这些以后,接下来我们便要修改我们的代码:
不难看出,我们可以利用指针的方法查找到地址,间接修改。有了这个思路以后,我们 便可可以修改我们的代码 :
//形参 void swap1(int x, int y) { int tmp = 0; tmp = x; x = y; y = tmp; } swap2(int* pa, int* pb) { int tmp = *pa; *pa = *pb; *pb = tmp; } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); printf("交换前:a=%d,b=%d\n", a, b); //实参 //swap1(a, b); swap2(&a, &b); printf("交换后:a=%d,b=%d\n", a, b); return 0; }
代码运行结果:
3.函数的参数
3.1实际参数(实参):
真实传递给函数的参数,叫实参。
实参可以是:常量、变量、表达式、函数等。
无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传给形参。
3.2形式参数(形参):
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
上面 Swap1 和 Swap2 函数中的参数 x , y , px , py 都是 形式参数 。在 main 函数中传给 Swap1 的 num1 , num2 和传 给 Swap2 函数的 &num1 , &num2 是 实际参数 。这里我们对函数的实参和形参进行分析:代码对应的内存分配如下:
这里可以看到 Swap1 函数在调用的时候, x , y 拥有自己的空间,同时拥有了和实参一模一样的内容。
所以我们可以简单的认为: 形参实例化之后其实相当于实参的一份临时拷贝。4.函数的调用
函数的调用一般分为两种
- 传值调用
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。- 传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。上面讲到的swap1其实就是传值调用,swap2就是传址调用
练习
写一个函数判断一个数是不是素数
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<math.h> //写一个函数可以判断一个数是不是素数 //is_prime(i) //返回1 表示是素数 //返回0 表示不是素数 int is_prime(int n) { //2~n~1试除 //2~sqrt(n)试除 int j = 0; for (j = 2; j <= sqrt(n); j++) { if (n % j == 0) { return 0; } } return 1; } int main() { //打印100~200之间的素数 int i = 0; for (i = 100; i <= 200; i++) { //判断i是否为素数 if (is_prime(i) == 1) { printf("%d ",i); } } }
写一个函数判断一年是不是闰年
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> //写一个函数判断一年是不是闰年 int is_leap_year(int y) { if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)) return 1; else return 0; } int main() { //1000~2000年的闰年 int y = 0; for (y = 1000; y <= 2000; y++) { //判断y是不是闰年 if (is_leap_year(y) == 1) { printf("%d ", y); } } return 0; }
那这段代码是否可以简化呢?
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> //写一个函数判断一年是不是闰年 //int is_leap_year(int y) //{ // if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)) // return 1; // else // return 0; //} int is_leap_year(int y) { return ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0); } int main() { //1000~2000年的闰年 int y = 0; for (y = 1000; y <= 2000; y++) { //判断y是不是闰年 if (is_leap_year(y) == 1) { printf("%d ", y); } } return 0; }
写一个函数,实现一个整形有序数组的二分查找
//写一个函数,实现一个整形有序数组的二分查找 //找到了就返回下标 //找不到返回-1 int binary_search(int arr[], int k, int sz) { int left = 0; int right = sz - 1; while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] < k) { left = mid + 1; } else if (arr[mid] > k) { right = mid - 1; } else { return mid; } } return -1;//找不到 } int main() { // //数组在传参的时候,传递不是整个数组 //传递是数组首元素的地址 // int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int k = 0; scanf("%d", &k);//要查找的元素 int sz = sizeof(arr) / sizeof(arr[0]); int ret = binary_search(arr, k, sz); if (-1 == ret) printf("找不到\n"); else printf("找到了,下标是:%d\n", ret); return 0; }
写一个函数,每调用一次这个函数,就会将 num 的值增加1
//写一个函数,每调用一次这个函数,就会将 num 的值增加1 void test(int* p) { //*p = *p + 1; (*p)++; } int main() { int num = 0; test(&num); printf("%d\n", num); return 0; }
5.函数的嵌套调用和链式访问
函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的
(简单来说就是A可以调用B,B可以调用A)
5.1嵌套调用
#include <stdio.h>
void new_line()
{
printf("hehe\n");
}
void three_line()
{
int i = 0;
for(i=0; i<3; i++)
{
new_line();
}
}
int main()
{
three_line();
return 0; }
5.2链式访问
把一个函数的返回值作为另外一个函数的参数。
#include <stdio.h>
#include <string.h>
int main()
{
char arr[20] = "hello";
int ret = strlen(strcat(arr,"bit"));//这里介绍一下strlen函数
printf("%d\n", ret);
return 0; }
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
//结果是啥?
//注:printf函数的返回值是打印在屏幕上字符的个数
return 0; }
加油啦,小比特~
记得一键三连嗷!三连!!三连!!!