【C语言】函数讲解(上)
- 1.函数是什么
- 2.C语言中函数的分类
- 2.1库函数
- 2.1.1如何学会使用库函数
- 2.2.自定义函数
- 3.函数的参数
- 3.1实际参数(实参)
- 3.2形式参数(形参)
- 4.函数调用
- 4.1传值调用
- 4.2传址调用
- 4.3练习
所属专栏:C语言
博主首页:初阳785
代码托管:chuyang785
感谢大家的支持,您的点赞和关注是对我最大的支持!!!
博主也会更加的努力,创作出更优质的博文!!
关注我,关注我,关注我,重要的事情说三遍!!!!!!!!
1.函数是什么
数学中我们常见到函数的概念。但是你了解C语言中的函数吗?
维基百科中对函数的定义:子程序
- 在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method,
subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组
成。它负责某项特定任务,而且相较于其他代 码,具备相对的独立性。 - 一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
2.C语言中函数的分类
C语言中把常用的功能,进行了封装,分装成一个个函数,提供出来大家一起使用。
比如我们常写的打印函数printf,输入函数scanf,等等。
注:C语言并不是直接实现库函数的,而是提供了C语言的标准和库函数的约定。
库函数的实现一般是由编译器去实现的。
2.1库函数
为什么会有库函数?
- 我们知道在我们学习C语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想把这个结果打印到我们的屏幕上看看。这个时候我们会频繁的使用一个功能:将信息按照一定的格式打印到屏幕上(printf)。
- 在编程的过程中我们会频繁的做一些字符串的拷贝工作(strcpy)。
- 在编程是我们也计算,总是会计算n的k次方这样的运算(pow)。
像上面我们描述的基础功能,它们不是业务性的代码。我们在开发的过程中每个程序员都可能用的到,为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。
也就是说我们把频繁使用的功能代码块分装成一个个的可供大家简单的使用的一个功能,这个功能就是我们的库函数,而我们又把这写功能函数一起分装在我们头文件中,所以一但我们想要使用这个功能函数的时候,就只需要引入头文件就行了。
这就是为什么什么我们每次写代码的时候都需要包含头文件的原因。
注:
但是库函数必须知道的一个秘密就是:使用库函数,必须包含 #include 对应的头文件。
这里对照文档来学习上面几个库函数,目的是掌握库函数的使用方法。
2.1.1如何学会使用库函数
为了让大家能够更好的使用库函数以及学习到更多的函数,这里提供一个搜索各种C原因的库函数
- 链接: https://cplusplus.com/
打开界面后点击画圈部分(这个是老版本的意思,因为新版本不支持搜索功能)
进入界面后就可以在里面进行搜索了。 - http://en.cppreference.com(英文版)
- http://zh.cppreference.com(中文版)
以上就是三个学习库函数的网站,大家要好好的学习噢。
2.2.自定义函数
如果库函数能干所有的事情,那还要程序员干什么?
所以更加重要的是自定义函数。
自定义函数和库函数一样,有函数名,返回值类型和函数参数。
但是不一样的是这些都是我们自己来设计。这给程序员一个很大的发挥空间。
函数的组成
ret_type fun_name(para1, * )
{
statement;//语句项
}
ret_type 返回类型
fun_name 函数名
para1 函数参数
我们来写一个函数试一试:
#include <stdio.h>
//get_max函数的设计
int get_max(int x, int y)
{
if(x>y)
return x;
else if(y>x)
return y;
}
int main()
{
int num1 = 10;
int num2 = 20;
int max = get_max(num1, num2);
printf("max = %d\n", max);
return 0;
}
这里就设计了一个比较大小的函数。
有的同学就会问了,为什么我们不直接把这个比较函数写在我们的主函数里面,这样不是更方便面。但是我想要说的是,如果我们的比较式子不止一个,而是有100个1000个甚至使更多的话那我们不是写更多的代码吗?反而如果我们写函数的话我们只需要调用就行了,然后把参数改一下。
所以函数是用于处理一系列相同类型的表达式或者算法的。
你可以把函数形象成一个工厂,你把参数传过去就相当于个工厂提供原料,你提供不同的原料就可以得到不同的产品,我们的返回值就是我们把生产好的产品给提供会去。
3.函数的参数
3.1实际参数(实参)
真实传给函数的参数,叫实参。
实参可以是:常量、变量、表达式、函数等。
无论实参是何种类型的量,在进行函数调用时,
它们都必须有确定的值,以便把这些值传送给形参。
就像我们上面的比较大小的函数中**int max = get_max(num1, num2);**这里的num1和num2就是我们的实参。
3.2形式参数(形参)
形式参数是指函数名后括号中的变量,因为形式参数
只有在函数被调用的过程中才实例化(分配内存单元),
所以叫形式参数。形式参数当函数调用完成之后就自动
销毁了 。因此形式参数只在函数中有效。
上面的比较函数中==int get_max(int x, int y)==这里的x和y就是我们的形参。
现在如果我想要你使用一个函数交换两个变量的值,该这么做呢?
你可能会这么交换:
void swap(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int m = 10;
int n = 20;
printf("m=%d,n=%d\n", m, n);
swap(m, n);
printf("交换后的值\n");
printf("m=%d,n=%d", m, n);
return 0;
}
}
代码结果:
你会发现他们并没有交换,这是为什么讷?
我们先看一下它们在内存发分布:
我们看到了虽然我们在执行到函数的内部的时候我们的x和y的值确实式交换了值,但是你会发现函数部分形参的地址与实参的地址是不同的,而我们之前也提过了变量的作用域的概念(进入生命周期创建,出了生命周期销毁)也就是所虽然我们在函数里面确实是交换了值,但是一旦出了函数x和y变量所在的内存空间就销毁了,而回到了main函数里面,但是销毁了x和y并没有影响到我们的m和n,所以他们的值并没有发生改变。
而正确的写法是传地址过去:
void swap(int* x, int* y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
int main()
{
int m = 10;
int n = 20;
printf("m=%d,n=%d\n", m, n);
swap(&m, &n);
printf("交换后的值\n");
printf("m=%d,n=%d", m, n);
return 0;
}
代码展示:
这里传地址过去涉及到了指针的概念,这里我们先简单的讲一点指针的只是,到后面会有详细的指针讲解。
我们取出地址的是&(取地址符号),如果我们要定义一个指针,所谓指针同样也是一个变量叫做指针变量,既然是变量那么就有类型,指针的类型是指针变量用一个 * 号表示。
如果是int型那就是:int * m。如果是double型的那就是 double * m;
上述的m和n的类型是int型的,所以他们的指针变量的类型就是int ,所以形参的类型也是如此。
如果我们想要通过地址找到里面的址的话就要通过接应用操作来拿到里面的值(接应用操作)
这是因为形参是实参是一份临时拷贝。
==对形参的修改不会影响实参。 ==
这里之所以需要传地址,是因为我们通过传地址,通过解引用操作直接通过地址找到这块地址所指向的的值。这样就可以改变了这块地址所指向的值了。
传地址你可以认为是我给一个工厂提供原料,工厂给我生产产品,工厂生产完产品是在我给他们送回的地址之后才开始做到,不然他们就算做好了产品也不知道要把这个产品送到哪里去。
4.函数调用
4.1传值调用
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
传值顾名思义就是将一个值传过去。
特点:形参是实参的一份临时拷贝,形参的改变不会影响实参。
4.2传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操
作函数外部的变量。
传址顾名思义就是把地址传过去,这个时候解引用操作会改变实参。
4.3练习
- 写一个函数判断是否是素数:
#include <stdio.h>
#include <math.h>
int isPrime(int num)
{
int i = 0;
for (i = 2; i < sqrt(num); i++)
{
if (num % i == 0)
return 0;
}
return 1;
}
int main()
{
int i = 0;
scanf("%d",&i);
int ret=isPrime(i);
if (ret == 1)
printf("是素数\n");
else
printf("是素数\n");
return 0;
}
- 写一个函数判断一年是否是润年:
#include <stdio.h>
int isLeap(int year)
{
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
return 1;
return 0;
}
int main()
{
int year = 0;
printf("请输入年份:>");
scanf("%d", &year);
int ret = isLeap(year);
if (ret == 1)
printf("是闰年\n");
else
printf("不是闰年\n");
return 0;
}
- 写一个函数,实现一个整数有序数组的二分查找。
#include <stdio.h>
int binary_number(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)
{
right = mid - 1;
}
else if (arr[mid] < k)
{
left = mid + 1;
}
else
{
return mid;
}
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 8;
int sz = sizeof(arr) / sizeof(arr[0]);
int ret = binary_number(arr, k, sz);
if (ret >= 0)
{
printf("下标是%d\n", ret);
}
else
{
printf("找不到\n");
}
return 0;
}