文章目录
函数的概述
- C源程序是由函数组成
- 函数是C源程序的基本模块
- 所有的函数定义(包括主函数main在内)都是平行的,但函数之间允许相互调用,也允许相互调用
- 把调用者称为主调函数,把被调用着称为被调函数
- 函数还可以自己调用自己,称为递归调用
- 一个源程序有且仅有一个main函数。main函数自身不能递归调用
函数分类(一)
从函数定义的角度:
库函数
- 由C系统提供无须用户定义,只须在程序前使用#include编译预处理命令
用户自定义函数
- 用户按需要编写的函数
函数分类(二)
从函数兼有其他语言的函数和过程两中功能中看:
有返回值的函数
此函数被调用后,将向调用者返回一个结果,称为函数返回值
- 函数值只能通过return语句返回给主调函数
- 函数值的类型与函数定义中函数的类型应保持一致,如果两者不一致,则以函数类型为准,这时会自动进行类型转换
- 一旦函数被定义为空类型,就不能在主调函数中使用被调函数的函数值了
无返回值的函数
只用于完成某项特定的任务,完成任务后,不向调用者返回函数值
函数分类(三)
从主调函数和被调函数之间数据传送的角度看
无参函数
函数定义,函数说明及函数调用中均不带参数,主调函数和被调函数之间不进行参数的传递
👉无返回值时“类型标识符”用void
无参函数定义形式
类型标识符 函数名() //函数首部 //类型标识符用于指定函数返回值的类型
{ //大括号内各行称函数体
声明部分 //声明部分用于定义所需变量
执行部分 //函数核心,完成特定的任务
}
有参函数
函数定义,函数说明中都有参数,称为形参
函数调用时也得有参数,称为实参,函数调用时把实参传给形参
👉形参既然是变量,必须给出类型说明
有参函数定义形式
类型标识符 函数名(形参列表) //各参数之间用逗号分隔
{
声明部分
执行部分
}
两者关系:
- 形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用
- 实参出现在主调函数中,进入被调函数后,实参也不能使用
- 形参和实参的功能时进行数据传递
形参和实参的特点:
- 形参只有在函数被调用时才分配内存单元,函数调用结束,释放所分配的内存单元
- 对形参的修改是不会影响实参的,形参实例化之后其实相当于实参的一份临时拷贝
- 实参可以是常量,变量,表达式,函数等。它们必须具有确定的值,以便把这些值传递给形参
- 实参和形参个数相等,类型一致,并按顺序一一对应
- 函数调用中发生的数据传送是单向的,即只能把实参的值传送给形参
数组作为函数参数
数组可以用作函数的参数,进行数据传送,数组传参,传过去的不是整个数组,而是第一个元素的地址,有两种形式,
把数组元素作为实参使用
- 数组元素是下标变量,他等同与普通变量
- 调用函数时,把作为实参的数组元素的值传递给形参,实现单向的传递
- 也就是函数调用时发生的值传递是把实参变量的值赋给形参
- 数组类型与函数的形参变量的类型一致,那么作为下标变量的数组元素的类型也与函数形参变量的类型一致
- 因此,不要求被调函数的形参是下标变量
判别一个整型数组中各个元素的值,大于0,输出各值,否则输出0
#include<studio.h>
void nzp(int v)
{
if(v>0)
printf("%d",v);
else
printf("%f",0);
}
main()
{
int a[5],i;
printf("输入5位数字")
for(i=0;i<5;i++)//for语句输入各元素,以各个元素作为实参,
{
scanf("%d",&a[i]);
nzp(a[i]);//输入一次元素,调用一次函数
}
}
把数组名作为实参使用
- 要求形参和实参都必须是类型相同的数组,两者都必须有明确的数组说明
- 编译系统不为形参数组分配地址,实际上,形参数组不存在,编译系统不为形参数组分配地址
- 数组名就是数组的首地址,所进行的传递只是地址的传递,实际上,形参数组和实参数组为统一数组,共享一段内存空间
- 形参数组和数组长度可以不一致,因为调用时,只传递首地址
- 函数形参表中,允许不给出形参数组的长度,或用变量表示数组元素的个数
void nzp(int a[],int n)→形参数组a未给长度,而有n值动态表示数组长度,n的值由主调函数的实参进行传送
多维数组也可以作为函数的参数,注意,第二维的长度不能省略
数组a中存放一个学生5门课程,求其平均成绩
#include<studio.h>
float aver(float a[5])//定义了一个实型函数,形参为实型数组
{
int i;
float av,s=a[0];
for(i=1;i<5;i++)
s=s+a[i];//循环求其总成绩
av=s/5;
return av;
}
void main()
{
float sco[5],av;
int i;
printf("输入 5门课程成绩");
for(i=0;i<5;i++)
scanf("%f"&sco[i]);//键盘输入五位数字
av=aver(sco);//sco作为实参调用aver函数
printf("平均成绩是",av)
}
实例训练一
打印100-200之间的素数
int sushu(int n)
{
int j=0;
for(j=2,j<n,j++)
{
if(n%j==0)
return 0;
}
return 1;
]
int main()
{
int i=0;
for(i=100;i<=200;i++)
{
if(sushu(i)==1)
printf("%d",i);
}
return 0;
}
实例训练二
实现一个整型有序数组的二分查找
int chazhao(int arr[],int k,int sz)
{
//int sz=sizeof(arr)/sizeof(arr[0])传参时不能这样求元素数组个数,这里arr时指针,指针大小为4
int left=0;//左下标
int right=sz-1;//右下标
while(left<=right)
{
int mid=(left+right)/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,11};
int k=3;
int sz=sizeof(arr)/sizeof(arr[0])//得在主调函数中求数组元素个数
if(chazhao(arr,k)==-1,sz)
{
printf("没找到")
}
else
{
printf("找到了");
}
}
实例训练三
写一个函数,每调用一次这个函数,就会将num的值加1
void add1(int* p)//p时指针变量。* p代表下面num
{
(*p)++//++的优先级高,所以得括起来*P。让num++
}
int main()
{
int num=0;
add1(num);//函数想要操作num,采用传址的形式
printf("num=%d\n",num);
add1(num);
printf("num=%d\n",num);
add1(num);
printf("num=%d\n",num);
}
函数的调用
- 函数名(实参表)
- 传值调用
函数的形参和实参分别占用不同内存块,对形参的修改不会影响实参
- 传址调用
地址+1,向后走一步
传址调用时把甘薯外部创建变量的内存地址传递给函数参数的一种调用函数的方式
这种传参方式可以让函数和函数外边的变量建立起真正联系,也就是函数内部可以直接操作函数外部的变量
函数调用的方式
- 函数表达式→以函数返回值参与表达式的运算,这种方式必须有返回值
- 函数语句→把函数调用作为一个语句
- 函数实参→函数作为另一个函数调用的实参出现,把该函数的返回值作为实参进行传送,因此该函数必须有返回值
链式访问
把一个函数的返回值作为另外一个函数的参数
printf("%d",printf("%d",printf("%d",43)))
第三个为43→2字符
第二个为2→1字符
第一个为1→输出4321
打印结果:4321
函数的嵌套调用
c语言中,不允许函数的嵌套调用,但允许一个函数的定义中出现对另一个函数的调用
嵌套调用实例训练
求三个数中最大值和最小值的数差
#include <studio.h>
//函数声明,如果未声明,自上而下运行,找不见被调函数,建议函数放前面
int dif(int x,int y,int z);
int max(int x,int y,int z);
int mix(int x,int y,int z);
mian()
{
int a,b,c,d;
scanf("%d%d%d"&a,&b,&c);
d=dif(a,b,c);//主调函数
printf("MAX-MIX=%d\n",d)//结论输出
}
int dif(int x,int y,int z)//被调函数1
{
return max(x,y,z)-mix(x,y,z);
}
int max(int x,int y,int z)//被调函数2
{
int r;
r=x>y?x:y;
return(r>z?r:z);
}
int min(int x,int y,int z)//被调函数3
{
int r;
r=x<y?x:y;
return(r<z?r:z);
}
函数的递归调用
程序调用自身的编程技巧称为递归,一个函数直接或间接地调用自身,称为递归调用
思考方式:大事化小
- 为了防止递归调用无休止进行,存在限制条件,当满足这个限制条件时候,递归不在继续,
- 每次递归调用之后越来越接近这个限制条件
- 主调函数也是被调函数,执行递归函数将反复调用其自身
- 可能会栈溢出
int f(int x)
{
int y,z;
z=f(y);//调用自身
return 2*z
}
接受一个整型值,按照顺序打印他的每一位
void num(int a)
{
if(a>9)//限制条件
{
num(a/10);//调用自身,直到< 9
}
printf("%d",a%10)
}
int main()
{
int shuzi=0;
scanf("%d",&shuzi);
//递归调用
num(shuzi);
}
描述第n个斐波那契数列(1.1.2.3.5.8.13.34。。。)第三个数等于前两个数和
int Fib(int n)
{
if(n<=2)//前两个数
return 1;
else(n>2)//重复计算量大
return Fib(n-1)+Fib(n-2)
}
int main()
{
int n= 0;
int ret =0;
scanf("%d",&n)
ret=Fib(n);
printf("ret=%d\n0",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\n0",ret)
return 0;
}
花神博客生涯之C语言(6)结束了哦~
接下来会持续更新(⊙o⊙)!