一、函数是什么
1、c语言的函数分类
1、库函数
2、自定义函数
二、库函数
![在这里插入图片描述](https://img-blog.csdnimg.cn/c85980829ea649e4932ecd9ee739722d.png
1、常用库函数
(1)io函数(in/out put)
<stdio.h>下 全是io函数
(2)字符串操作函数
用来操作字符串 例如:
strlen-string -length–与字符串长度有关
(3)strcpy-string copy-字符串拷贝
“\0”也会被拷贝
大小写转换
(4)内存操作函数
memset
格式:memset(要设置的地址,‘要设置成的内容’,设置的个数);
例如:将arr中的前5个字符设置成*号
int main()
{
char arr[]="hello world";
memset(arr,'*',5);
printf("%s\n",arr);
return 0;
}
(5)时间/日期函数
time函数
(6)数学函数
开平方函数
(7)其他库函数
三、自定义函数
1、格式:
返回类型,函数名(函数参数,参数)
{
函数体:编写函数的实现方式
}
返回类型:ret_type
函数名称:fun_name
函数参数:para1
例子1:定义两个函数取最大值
命名为get_max函数
int get_max(int x,int y)
{
if(x>y)
{
return x;
}
else
{
return y;
}
}
int main()
{
int a=10;
int b=20;
int max=max(a,b); //也可以不定义a和b的值,直接在括号中输入数字即可
printf("max =%d\n",get_max); //此处使用的函数名称 get_max,应该和上方自定义的函数名称一致
return 0;
}
例子2:写一个函数可以交换两个整型变量的内容
命名为swap函数
没有可返回的值,不需要返回值,此时自定义函数时,使用void(无类型,空;无返回值)
void swap(int*pa , int*pb)
{
int tmp=0;
tmp=*pa;
*pa=*pb; //解引用操作符
*pb=tmp;
}
int main()
{
int a=10;
int b=20;
printf("a=%d b=%d",a,b);
swap(&a,&b); //注意此处是取a、b的地址,而非取a和b值
printf("a=%d b=%d",a,b);
return 0;
}
该代码的运行流程
四、函数的参数
**重要结论:*当实参传给形参时,形参是实参的临时拷贝;但是对形参的改变并不会影响实参
1、实际参数
函数:swap(100,get_max(1,4)),前提是get_max函数和swap函数已经定义好了
2、形式参数
形式上的参数,被调用时才有用,只能在函数内部使用,出了函数内部将会被销毁
(只是一个壳子,里面放谁都可以)
五、函数的调用
使用自定义函数或者库函数
1、传值调用
类似swap1
函数的形参和实参占用不同内存块(地址不同),对于形参的修改并不会影响实参。
使用情况:
不需要改变时,用传值即可
2、传址调用
数组传参时,是将数组第一个元素的地址传入,而非整体传入。
类似swap2
将自定义函数中传入实参的地址,再通过解引用操作符来改变主函数中的实参
如果只是传值调用,改变形参无法改变主函数中的实参。
使用情况:
要改变函数外部变量,用传址调用
六、函数的嵌套调用和链式访问
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;
}
2、链式访问
定义:把一个函数的返回值当作另外一个函数的参数
int main()
{
printf(%d,printf(%d,printf(%d,43)));
return 0;
}
七、函数的声明和定义
当函数定义(自定义函数)在主函数之后时,需要在主函数之前进行函数声明
函数先声明后使用
头文件放入函数声明
源文件放入函数定义
声明格式:
引用自己的头文件格式:include“ 自定义名称.h”
使用场景:
函数声明、自定义函数(函数实现)、主函数(拿来用)不再同一个文件夹中时
一般是,对于大型项目分工时,所有人有可能调用相同的函数,但又不能同时写入一个文件中。
此种情况适用
当要使用该函数时,直接加头文件即可
引用自己的头文件格式:include“ 自定义名称.h”
八、函数递归(7.22)
什么是递归
常见错误:stack overflow (栈溢出,空间被占满)
每次函数调用都会分配空间
内存分区:栈区(函数形参、局部变量)、堆区(动态开辟内存malloc、calloc)、静态区(全局变量、static修饰的变量)
递归的两个必要条件
递归有能力自己停下来
递归举例
练习1:接收一个整型值(无符号),按照顺序打印它的每一位。例如:输入1234,输出1,2,3,4.
void print(int num)
{
if(num>9)
{
print(num/10);
}
printf("%d",num%10);
}
int main()
{
int num=0;
scanf("%d",&num);
print(num);
return 0;
}
插图43‘
练习2:编写函数(不允许创建临时变量),求字符串长度
int my_strlen(char* str)
{
if(*str!='\0')
{
return 1+my_strlen(str+1); //str+1为数组地址+1,重新进入循环
}
else
{
return 0;
}
}
int main()
{
char arr[]="bit";
int len=my_strlen(arr); //模拟实现一个strlen函数
printf("len =%d\n",len); //arr是数组,是数组传参,传过去的不是整个数组而是数组的第一个元素的地址
return 0;
}
插图7’13‘’
递归与迭代
迭代与循环类似,但是展现形式不一定是循环
练习3:求n的阶乘(不考虑溢出)
int fac2(int n)
{
if(n<=1)
{
return 1;
}
else
{
return n*fac2(n-1);
}
}
int main()
{
int n=0;
int ret=0;
scanf("%d",&n);
ret=fac2(n); //fac1函数的返回值是ret
printf("%d\n",ret);
return 0;
}
练习4:求第n个斐波那契数
//递归方式(会重复计算,占用大量资源,不推荐)
int fib(int n)
{
if(n<=2)
{
return 1;
}
else
{
return fib(n-1)+fib(n-2);
}
}
int main()
{
int n=0;
int ret=0;
scanf("%d",&n);
ret=fib(n);
printf("ret =%d\n",ret);
return 0;
}
//迭代方式
int fib(int n)
{
int a=1;
int b=1;
int c=1;
while(n>2)
{
c=a+b;
a=b;
b=c;
n--;
}
return c;
}
int main()
{
int n=0;
int ret=0;
scanf("%d",&n);
ret=fib(n);
printf("ret =%d\n",ret);
return 0;
}
九、零碎知识
vs2010软件 纠错方式:
F10,逐过程
F11,逐语句(可以查看自定义函数的过程)