提示:在本章节内容学习,小编希望大家对于c语言函数有所了解掌握,同时希望在跟随小编学习的过程中大家能自己动手敲代码。如果大家觉得小编讲的不错,可以抬起大家发财的手给作者一个赞点个关注,作者后续会持续更新有关c语言的内容知识梳理。如果有什么信息缺漏,大家可以在评论区给我留言,小编会及时回复以及更改。
提示:在本章节内容,小编将会将c语言的函数部分系统的讲述一遍,希望大家拿起自己的电脑跟随小编一起开启函数的学习之旅:
本节需要用到的相关教材:
c语言库函数参考文档
提示:以下是本篇文章正文内容
1、在计算机中,函数是一个大型程序中的某部分代码,由一个或者多个语句块组成。它负责完成某项特定任务,而是相比较于其他代码,具有相对独立性。
2、简单的说,函数是c语言的功能单位,实现一个函数可以通过封装一个函数来实现。所以在我们创建定义函数的时候一切要以功能为单位,根据功能去确定函数的参数与返回值。
3、c语言的程序其实是由无数个小的函数组合而成的,就是说当你在运行计算机的时候去计算一个很大的计算任务,它是由一个个小的函数去组成并完成这个计算任务的。
从函数定义角度来分类(也就是函数由谁实现):我们可以分为三大类
函数 | 由谁实现 |
---|---|
库函数 | 由c库实现的 |
自定义函数 | 程序员自己编写来实现的 |
系统调用 | 操作系统实现的函数 |
这里我们介绍一下大家容易混淆的库函数的一些基本内容:
1、首先c语言标准中规定了c语言的各种标准,但是c语言它并不提供库函数;在c语言国际标准ANSIC规定了一些常用的函数的标准,被称为标准库 。
2、那什么是c语言库函数呢:不同的编译软件它会有编译器厂商,编译器厂商就会根据标准库的标准给出一系列函数的实现,这就称为
库函数。
3,使用库函数,必须包含 #include对应的头文件。
1,ret_type这里是函数的返回类型
2,fun_name这里是函数名
3,(形式参数)这里是函数参数
4,{}里面的是函数体,也就是你写的这个函数的功能实现
5,带返回值的函数在定义函数的时候,必须带着返回值类型,在函数体里,必须有 return如果没有返回值类型,默认返回整型
#define _CRT_SECURE_NO_WARNINGS
#include
//函数定义
int get_max(int x, int y)
{
if (x > y)
return x;
else
return y;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d%d", &a, &b);
//比较大小
int x = get_max(a, b); //函数的调用
printf("%d\n", x);
}
printf("%d\n",x);
}
接下来我们来分析一下这个代码:
首先main函数是整个程序的入口,我们从main函数进入
1,这里需要判断两个整数的大小,所以我们先定义两个整型,输入两个整数。
2,输入两个整数后,我们需要对两个整数进行判断,这里需要一个函数来对他进行判断
3,我们定义一个函数 get_max,我们在2中知道传入两个整数,所以它的参数位置为两个整型的变量,最后我们判断大小,判断完成我们需要返回较大值,较大值类型为a,b中一个,所以返回类型为整型。
4,我们在调试的可以观察到,x和y确实得到了a和b的值,但是x和y的地址和a和b的地址是不⼀样的,所以我们可以理解为形参是实参的⼀份临时拷⻉。
真实传给函数的参数,叫做实参。
实参可以是:变量,常量,表达式,函数等。
无论实参是何种类型的量,在进行函数调用的时候,他们必须都得有确定的值,方便传这些值给形参。
形参是变量,是被调函数的局部变量。
形式参数是指函数名后面括号中的变量,因为形参函数只有在函数被调用的过程中才被分配内存单元,所以叫形式参数。形式参数在函数调用完成后就被自动销毁了。因此形式参数只有在函数内部才有作用。
注意:实际参数和形式参数可以同名
接下来我们通过一串代码来了解一下函数的参数类型
#define _CRT_SECURE_NO_WARNINGS
#include
//交换两个整型变量
void swap(int x, int y)
{
int temp = 0;
temp = x;
x = y;
y = temp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d%d",&a,&b);
//交换前
printf("交换前:x=%d y=%d\n", a, b);
swap(a, b);
//交换后
printf("交换后:x=%d y=%d\n", a, b);
}
在这里我们可以发现,前后居然没有发生交换。这是因为传递给函数的a,b的地址与x,y的地址不同,他们都有各自的空间,如下图,他传递值给x,y,我们又在里面创建temp将x,y交换了值,但是它不影响a,b,因为他们是完全不同的空间。
提示:在这里a和b叫实参,x和y叫形参。将实参传给形参,形参将会是实参的一份临时拷贝,它们各自都有各自的独立空间,所以修改形参不会影响实参。所以在这里我们需要把实参的地址传给形参,这样通过地址指向实参的值改变形参就会影响到实参。
#define _CRT_SECURE_NO_WARNINGS
#include
//交换两个整型变量
void swap(int* x, int* y)
{
int temp = 0;
temp = *x;
*x = *y;
*y = temp;
}
int main()
{
int x = 0;
int y = 0;
scanf("%d%d",&x,&y);
//交换前
printf("交换前:x=%d y=%d\n", x, y);
swap(&x, &y);
//交换后
printf("交换后:x=%d y=%d\n", x, y);
}
在这里我们可以发现值发生了交换。通过这里我们可以发现,当形参改变对实参不需要产生影响实参时候不需要传递地址,当形参修改要影响实参的时候需要传递地址。
数组在传参需要注意
1,函数形式参数要和实参个数要匹配
2,函数的实参是数组,,形参也是可以写成数组形式的
3,形参如果是一维数组,数组大小可以省略不写
4,形参如果是二维数组,行可以省略,但是列不可以省略(最好都不省略)
5,数组传参,形参是不会创建新的数组的
6,形参的操作的数组和实参的数组是同一个数组
传值调用: 像上边例子中的get_max函数
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
传址调用: 像上边例子中的swap函数
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
练习1:写一个函数判断一个函数是不是素数(只能被1和它本身整除的数)
int prime(int x)
{
int n = 0;
for (n = 2; n < x - 1; n++)
{
if (x % n == 0) //此时x就不是素数
{
return 0;
}
}
return 1; //这个就是素数了
}
int main()
{
int i = 0;
scanf("%d", &i);
int z = prime(i);
if (z)
{
printf("奇数:%d", z);
}
return 0;
}
练习2:写一个函数判断是否为闰年(能被4整除并且不能被100整除 或者能被400整除就是闰年)
int leap_year(int x)
{
if (((x % 100 != 0) && (x % 4 == 0)) || (x % 400 == 0))
return 1;
else
return 0;
}
int main()
{
int year = 0;
scanf("%d", &year);
int m = leap_year(year);
if (m)
printf("闰年 =%d ", year);
return 0;
}
函数与函数之间可以根据实际要求进行组合的,也就是相互调用
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;
}
在这里小编创建了两个函数new_line和three_line,而小编在three_line中调用了new_line,这个就叫函数嵌套调用,在一个函数中调用另一个函数。但是函数的定义不能嵌套,即不能在一个函数体内定义另外一个函数,所有函数的定义都是平行的。
1,函数的链式访问依赖函数的返回值;就是把函数的返回值作为另一个函数的参数
#include
int main()
{
printf("%d\n",strlen("abcdef"));
return 0;
}
在这里strlen是一个求字符串长度的函数,这个小编在之前的博客讲过,strlen求出字符串的长度然后返回给printf函数用%d格式打印出来,strlen返回值给printf做了一个参数,像链条一样把这两个函数给串起来叫做链式访问
2,来个练习求输出结果
#include
int main()
{
printf("%d\n",printf("%d", printf("%d", 43)));
return 0;
}
在这里可以发现他的输出结果为4321,为什么呢?printf函数返回的是打印在屏幕上的字符的个数。
上⾯的例⼦中,我们就第⼀个printf打印的是第⼆个printf的返回值,第⼆个printf打印的是第三个printf的返回值。
第三个printf打印43,在屏幕上打印2个字符,再返回2
第⼆个printf打印2,在屏幕上打印1个字符,再放回1
第⼀个printf打印1
所以屏幕上最终打印:4321.
1,告诉编译器有一个函数叫什么,参数是什么,返回类型又是什么。但是具体存不存在,函数决定不了。
2,函数的声明一般出现在函数的使用前,要满足先声明后使用。
3,函数的声明一般放在头文件中。
接下来通过代码来描述,我们接着用上次求闰年的代码,只不过我们把函数定义放在main函数后面
int main()
{
int year = 0;
scanf("%d", &year);
int m = leap_year(year);
if (m)
printf("闰年 =%d ", year);
return 0;
}
int leap_year(int x)
{
if (((x % 100 != 0) && (x % 4 == 0)) || (x % 400 == 0))
return 1;
else
return 0;
}
在这里我们可以发现程序报出了一个警告,这是因为c语⾔编译器对源代码进⾏编译的时候,从第⼀⾏往下扫描的,当扫描到main函数中的leap函数时候,编译器他不认识,所以发出警告;这个问题该如何解决呢;我们只需要在程序也就是main函数上方给leap函数一个声明就可以,函数声明包含返回类型,参数类型和函数名,记住要加一个分号哦。
//函数的声明
int leap_year(int x);
int main()
{
int year = 0;
scanf("%d", &year);
int m = leap_year(year);
if (m)
printf("闰年 =%d ", year);
return 0;
}
//函数的定义
int leap_year(int x)
{
if (((x % 100 != 0) && (x % 4 == 0)) || (x % 400 == 0))
return 1;
else
return 0;
}
1,函数定义是指函数的具体实现,交代函数的功能实现。
2,函数的定义也是⼀种特殊的声明,所以如果函数定义放在调⽤之前也是可以的。
程序调用自身的编程技巧称为递归
递归作为一种算法,它的主要思考方式在于:把大事化小
1,递归存在限制条件,当满足这个限制条件的时候,递归便不在继续,为什么这么说呢,根据递归的概念,程序调用自身的编程技巧称为递归,函数在不断调用自己,不断在寻找自己,却跳不出来,所以它会一直递归下去成为一个死循环。
2,每次递归调用之后需要越来越接近这个限制条件。
//接受一个整型值(无符号),按照顺序打印它的每一位
//1234
//print(123) 4
//print(12) 3 4
//print(1) 2 3 4
//1 2 3 4
void print(unsigned int x)
{
if (x > 9)
{
print(x / 10);
}
printf("%d ", x % 10);
}
int main()
{
unsigned int i = 0;
scanf("%u", &i);
print(i);
return 0;
}