C语言基础:弟11天笔记
内容提要
函数
函数的调用
调用方式
①函数语句:
test();//对于没有返回值的函数,直接调用
int res =max(2,4) //对于有返回值的函数,一般需要在主调函数中接收被调函数的返回值
②函数表达式:
4 + max(2,4);
scanf("%d",&num)!=1;
(c=getcahr())!='\0'
③函数参数:
printf("%d",max(2,4));//将有返回值的函数作为实参
printf("%d",(int)fabs(num));
在一个函数中调用另一个函数具备以下条件:
int b()
{
}
int a()
{
b();
}
函数的声明
函数调用时,往往要遵循先定义后使用,但如果我们对函数的调用操作出现在函数定义之前,则需要对函数进行声明。
完整的函数分为三部分:
定义:
int max(int x,int y);//指明参数名称
int max(int ,int,double);//省略参数名称
int max(int x,int y,double z)//函数定义,不能省略参数的数据类型,参数个数,位置要和函数声明完全一致
{
return x>y?x:y>z?y;(int)z;
}
int main()
{
printf("%d\n",max(4,5,6));
}
函数声明的作用:
函数声明的使用
错误演示:被调函数写在主调函数之后
//主调函数
int main()
{
printf("%d\n",add(12,13));//此时编译会报编译错误,因为函数没有经过声明,编译系统无法检查函数调用的合法性
}
//被调函数
int add(int x,int y)
{
return x+y;
}
正确演示:被调函数写在主调函数之前
//被调函数
int add(int x,int y)
{
return x+y;
}
//主调函数
int main()
{
printf("%d\n",add(12,13));//
}
注意:如果函数的调用比较简单,并且被调函数写在主调函数之前,此时是可以省略函数声明的。
正确演示:被调函数黑主调函数无法区分前后,需要增加被调函数的函数声明
//函数声明
int add(int x, int y);//写法一
int add(int,int);//写法二
//被调函数
int add(int x,int y)
{
return x+y;
}
//主调函数
int main()
{
printf("%d\n",add(12,13));//
}
注意:如果涉及函数的相互嵌套作用,或者复杂嵌套调用,此时无法区分函数的前后,这就需要函数的声明。
int add(int a,int b)
{
}
int add(int ,int)
函数的嵌套使用
定义
函数不允许嵌套定义,允许嵌套调用
void(....){.....}
void b()
{
a();
}
void a()
{
void b()
{
}
}
嵌套调用:在被调函数内主动去调用其他函数,这样的函数调用形式,称为嵌套使用。
案例:
案例1
/*************************************************************************
> File Name: 1.c
> Author:
> Description:
> Created Time: 2025年03月17日 星期一 10时26分46秒
************************************************************************/
#include <stdio.h>
/**
*定义一个函数,求素数
*param n :需要校验素数的整数
*
*
* */
int is_prime(int n)
{
int k,i,flag=1;
//过滤素数:2~n-1
for(i=2;i<n-1/*n/2*/;i++)//在2~n-1,出现了能被正除的情况,就跳出,因为他已经是非素数
{
if(n%i==0)
{
flag=0;
break;
}
}
return flag;
}
int main(int argc,char *argv[])
{
for(int i=3;i<=100;i++)
{
if(is_prime(i))
printf("%-4d",i);
}
printf("\n");
return 0;
}
案例2
/*************************************************************************
> File Name: 2.c
> Author:
> Description:
> Created Time: 2025年03月17日 星期一 10时45分14秒
************************************************************************/
#include <stdio.h>
int max_2(int,int);
int max_4(int,int,int,int);
int max_2(int a,int b)
{
return a>b?a:b;
}
int max_4(int a,int b,int c,int d)
{
//写法1
/* int max;
max=max_2(a,b);
max=max_2(max,c);
max=max_2(max,d);*/
//写法2
return max_2(a,b)>max_2(c,d)?max_2(a,b):max_2(c,d);
}
int main(int argc,char *argv[])
{
int a,b,c,d;
printf("请输入4个整数:\n");
scanf("%d%d%d%d",&a,&b,&c,&d);
//接收最大值
int max=max_4(a,b,c,d);
printf("%d,%d,%d,%d中的最大值为%d\n",a,b,c,d,max);
return 0;
}
函数的递归调用
定义
递归调用的含义:在一个函数中,直接或者间接调用函数自身,就称之为函数的递归调用。本质上还是函数的嵌套调用。
//直接调用
a() →a()
// 间接调用
a()---->b()---->c()
a()---->b().....a();
递归调用的本质:
是一种循环结构,他不同于我们之前学的while、for、do....while循环结构,这些循环结构是借助于循环变量;而递归调用时利用函数自身实现循环结构,如果不加以控制,很容易产生死循环。
递归调用的注意事项:
①递归调用必须要有出口,一定要想办法终止递归(否则就会产生死循环)
②对终止条件的判断一定要放在函数递归之前(先判断,在执行)
③进行函数递归调用
④函数递归的同时一定要将函数调用向出口逼近。
案例
案例1
/*************************************************************************
> File Name: 3.c
> Author:
> Description:
> Created Time: 2025年03月17日 星期一 11时18分03秒
************************************************************************/
#include <stdio.h>
/**
*定义一个函数,求年龄
*param n:第n个人
*return 第n个人年龄
*/
int get_age(int n)
{
//创建一个变量,存储函数返回值,返回的是年龄
int age ;
//出口设置
if(n==1) //第一个人
age=10;
else if(n>1)
age=get_age(n-1)+2;//当前这个人的年龄=上一个人的年龄+2
return age;
}
int main(int argc,char *argv[])
{
printf("%d\n",get_age(5));
return 0;
}
案例2
/*************************************************************************
> File Name: 4.c
> Author:
> Description:
> Created Time: 2025年03月17日 星期一 14时11分42秒
************************************************************************/
#include <stdio.h>
/**
*定义一个函数,求n的阶乘
*@param n上限
*@return 阶乘运算结果
* */
size_t fac(int n)
{
size_t f;//定义一个变量。用作阶乘结果的接收
//出口校验
if(n<0)
{
printf("n的范围不能是0以下的数\n");
return -1;
}
else if(n==0||n==1)
{
f=1;//出口
}
else //n>1递归
f=fac(n-1)*n;
}
int main(int argc,char *argv[])
{
size_t n;
printf("请输入一个整数:\n");
scanf("%lu",&n);
printf("%lu的阶乘为%lu\n",n,fac(n));
return 0;
}
排序算法:快速排序
快速排序(Quick Sort)是一种高效的排序算法,采用分治法(Divide and Conquer)策略。它的核心思想是通过选取一个”基准值”(pivot),将数组分为两部分:一部分比基准值小,另一部分比基准值大,然后递归地对这两部分进行排序。
快速排序的基本步骤
1.选择基准值:
2.分区(Partition):
3.递归排序:
4.合并结果:
分析:
总结:
快速排序,实际上就是通过一个基准值不断拆分数组,一直拆到不能再拆为止(最后只剩一个元素)
代码:
/*************************************************************************
> File Name: 5.c
> Author:
> Description:
> Created Time: 2025年03月17日 星期一 15时06分31秒
************************************************************************/
#include <stdio.h>
/**
* 定义一个函数,实现快速排序
*@param arr:待排序数组
*@param n 出口
*/
void QSort(int arr[],int n)
{
if(n<2) //递归终止条件
return;
int i=0,j=n-1;//定义两个指针,i是从左往右,j是从右往左
int pivot =arr[0];// 选择第一个元素 作为基准值
while(i<j)
{
while(arr[j]>pivot&&i<j)
j--;
while(arr[i]<=pivot&&i<j)
i++;
//交换找到两个元素
int temp =arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
//i==j相遇,分区结束,基准值归位
int temp=arr[0];
arr[0]=arr[i];
arr[i]=temp;
//递归排序左半部分 <= 基准
QSort(arr,i);
//递归排序右半部分 >基准
QSort(arr+i+1,n-1-i);
}
int main(int argc,char *argv[])
{
int arr[]={23,45,56,24,14,78,22,19};
//排序
QSort(arr,sizeof(arr)/sizeof(arr[0]));
for(int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
printf("%-4d",arr[i]);
}
printf("\n");
return 0;
}
数组做函数参数
定义
当用数组做函数的实参时,则形参应该也要用数组或者指针变量来接收(函数实参是数组,形参一定是数组或者指针),注意的是,此次传递的并不代表传递了数组中所有元素数据,而是传递了第一个元素的内存地址(数组首地址),形参接收到这个地址后,则形参和实参就代表了同一个内存空间,则形参的数据修改会改变实参。这种数据传递方式称为地址传递
如果用数组作为函数的形参,那么我们提供另一个形参表示数组的元素个数。原因是数组形参代表的仅仅是实际数组的首地址。也就是说形参只获取到了实参数组的第一个元素的地址,并不确定传递了多少个数据。所以提供另一个形参表示数组元素的容量,可以防止在被调函数对实际数组访问时产生的下标越界。
//定义一个函数,将数组作为参数
void fun(int arr[],int len)//数组传参,形参只能接收到实参数组的首地址,并不是完整的数组
{
for(int i =0;i<len;i++)
{
printf("%-4d",arr[i]);
}
printf("\n");
}
void main()
{
int arr[]={11,22,33,44,55};
inr len =sizeof(arr)/sizeof(arr[0]);
fun(arr,len)
}
但有一个例外,如果是用字符数字做形参,且实参数组中存放的是字符串数据(形参是字符串数组,实参是字符串常量)。则不用表示数组元素个数的形参,原因字符串本身自动会自动结束标志\n,举例:
//定义一个函数,传递一个字符
void fun(char arr[])
{
char c;
int i=0;
while((c=arr[i])!='\0')
{
printf("%c\n",c);
i++;
}
printf("\n");
}
void main()
{
fun("hello");
}
案例
案例1
int a[10]={12,12,10,18,5};
int b[10]={111,112,110,8,5};
/*************************************************************************
> File Name: 6.c
> Author:
> Description:
> Created Time: 2025年03月17日 星期一 16时52分33秒
************************************************************************/
#include <stdio.h>
#define LEN 5
/***
*定义一个函数,实现两个数字的比较
*@param x,y 参与比较的两个数字
*@return 比较结果x>y返回1,x<y返回-1,x==y返回0
*
*/
int get_large(int x,int y)
{
int flag=0;
if(x>y)
flag=1;
else if (x<y)
flag=-1;
return flag;
}
int main(int argc,char *argv[])
{
//定义两个数组,循环变量,最大,最小,相等
int a[LEN]={12,12,10,18,5};
int b[LEN]={111,112,110,8,5};
int i ,max=0,min=0,k=0;
//遍历循环,进行比较
for(i=0;i<LEN;i++)
{
int res=get_large(a[i],b[i]);
if(res==1)
max++;
else if(res==-1)
min++;
else
k++;
}
printf("max=%d,min=%d,k=%d\n",max,min,k);
return 0;
}
案例2
/*************************************************************************
> File Name: 7.c
> Author:
> Description:
> Created Time: 2025年03月17日 星期一 17时02分33秒
************************************************************************/
#include <stdio.h>
float get_avg(float score[],int len)
{
int i;
float aver,sum=score[0];
//遍历数组
for (i=1;i<len;i++)
{
sum+=score[i];
}
aver =sum/len;
return aver;
}
int main(int argc,char *argv[])
{
//准备两个测试数组
float score1[]={66,78,86,56,46};
float score2[]={77,88,98,87,67,65,55,45,67,78};
printf("这个班的平均分:%6.2f\n",get_avg(score1,sizeof(score1)/sizeof(score1[0])));
printf("这个班的平均分:%6.2f\n",get_avg(score2,sizeof(score2)/sizeof(score2[0])));
return 0;
}