C语言 函数.docx
- 文档编号:5754047
- 上传时间:2022-12-31
- 格式:DOCX
- 页数:12
- 大小:22.78KB
C语言 函数.docx
《C语言 函数.docx》由会员分享,可在线阅读,更多相关《C语言 函数.docx(12页珍藏版)》请在冰豆网上搜索。
C语言函数
9函数
1个C源程序=1个主函数+若干子函数
【引例】
任意读入2个数,输出较大数。
intmax(a,b)/*子函数-被调函数*/
inta,b;/*形式参数、形参*/
{intc;
if(a>b)c=a;
elsec=b;
return(c);
}
main()/*主调函数*/
{intx,y,z;
scanf("%d%d",&x,&y);
z=max(x,y);/*实际参数、实参*/
printf("MAX=%d\n",z);
}
一、程序设计的总体思想
“自上而下、逐步细化”的模块化
函数就是一个个功能相对独立的模块。
二、函数的定义
1、C中函数的定义是独立且平等的,即:
“主、子函数的定义不分前后,且不可以嵌套定义。
”
2、使用函数,是因为任务的功能比较复杂。
3、函数的分类:
(1)有参函数
【书写格式】
格式一:
函数返回类型函数名(形参1,形参2)
对形参进行说明;
{函数体
return语句;
}
格式二:
函数返回类型函数名(类型名形参1,类型名形参2)
{函数体
return语句;
}
(2)无参函数
函数返回类型函数名()
{函数体
return语句;
}
另外的划分:
系统函数(库函数)和自定义函数;有返回值的函数和无返回值的函数。
4、例题:
哥德巴赫猜想
任意一个大偶数(>=4)都可以拆成两个素数之和。
【题目:
任意读入一个大偶数>=6,然后输出被拆成的两个素数。
】
/*主函数拆分,子函数判断素数*/
intPDSS(x)
longx;/*与主函数的同名*/
{intflag=1;
longk;
for(k=3;k<=x-1;k=k+2)if(x%k==0){flag=0;break;}
return(flag);
}
main()
{longx;
longs1,s2;
do
scanf("%ld",&x);
while(x<6||x%2!
=0);
s1=3;s2=x-s1;
while(!
PDSS(s1)||!
PDSS(s2))
{s1=s1+2;s2=s2-2;}
printf("%ld=%ld+%ld\n",x,s1,s2);
}
三、自定义子函数的使用说明
1、主、子函数的书写不分前后,但是当被调函数书写在主调函数的后面且返回值不为整型int时,必须在主调函数的说明部分(或在函数体外加以说明)对被调函数加以说明:
【格式】
返回类型名函数名();
2、主函数一般做主调函数(即使带参数,也只能自己调用自己),子函数通常做被调函数,但也可以做主调函数,调用别的子函数或自己。
3、程序的执行总是从主函数到主函数结束,除非遇到exit函数语句。
4、return语句的格式:
格式一:
return(表达式);
格式二:
return表达式;
格式三:
return;
【功能】格式一与格式二的功能完全相同:
将表达式的值(一个值)带回到主调函数的调用点,且立即终止所在函数的执行。
格式三只能终止所在函数的执行并返回主调函数的调用点,但不带回值(因此很少用,通常可以省略)。
四、实参与形参之间的关系
1、实参和形参在个数、类型、顺序上一一对应一致。
2、实参总是单向地传值给形参:
传的值有两种(内容或地址),一般,传内容时常常简称“值传递”,传地址时简称“地址传递”。
3、实参可以是常量、变量(通常有值)、表达式;形参通常是变量。
五、参数的地址传递
例1:
任意读入两个数,调用函数求得二者的和与积。
main()
{floatx,y,h,j;
voidHJ();
scanf("%f%f",&x,&y);
HJ(x,y,&h,&j);
printf("%f,%f\n",h,j);
}
voidHJ(a,b,p,q)
floata,b,*p,*q;
{*p=a+b;
*q=a*b;
}
1、当被调函数没有返回值时,定义时函数名前加void,在主调函数中调用语句的格式:
被调函数名(参数系列);省略类型名时,默认int型。
2、当需要从被调方获得一个以上的值时,通常使用地址传递。
例2:
任意读入10个数,然后进行升序排列。
【分工:
】
main()
{inta[10],i;
voidPX();
for(i=0;i<10;i++)
scanf("%d",&a[i]);
PX(a,10);
for(i=0;i<10;i++)
printf("%d\n",a[i]);
}
voidPX(p,n)
int*p,n;
{inti,j;intt;
for(j=1;j<=n-1;j++)
for(i=0;i<=n-1-j;i++)
if(*(p+i)>*(p+i+1))
{t=*(p+i);*(p+i)=*(p+i+1);
*(p+i+1)=t;}
}
【说明】
(1)当需要将多个值传给被调函数时,通常使用数组名作为实参,同时为了增大被调函数的灵活性,将数组元素的个数也作为实参。
(2)与数组名这个实参对应的形参的格式有两种:
①指针变量;②通常省略了元素个数的数组。
系统会主动将第二种格式转换成相应的指针。
voidPX(intp[],intn)
(3)在形参中出现的一维数组,通常省略元素的个数,系统立刻将之转化成相应的指针变量,本例中即为:
int*p
(4)实参还可以是二维数组的名字,对应形参可以有两种格式:
①行指针变量;②通常省略了第一维的行数的二维数组。
系统会主动将第二种格式转换成相应的指针。
例3:
主函数生成如下5阶方阵,然后调用子函数求得副对角线上所有元素之积,以及方阵所有元素之和,再由主函数输出。
1
2
3
4
5
1
2
3
4
5
1
2
3
4
5
1
2
3
4
5
1
2
3
4
5
intfun(p,he)
int(*p)[5],*he;
{intji=1;
inti,j;
for(i=0;i<5;i++)
ji=ji*p[i][4-i];
*he=0;
for(i=0;i<5;i++)
for(j=0;j<5;j++)
*he=*he+p[i][j];
returnji;
}
main()
{inta[5][5],i,j;
intji,he;
for(i=0;i<5;i++)
for(j=0;j<5;j++)
a[i][j]=j+1;
ji=fun(a,&he);
printf("JI=%d,HE=%d\n",ji,he);
}
intfun(p,he)
intp[][5],*he;
【归纳】
形参和实参之间的联系方式(单向传递:
实参→形参)
传递方式
形参
实参(同类型的)
说明
通过函数调用能否改变实参的值
值传递
变量
常量、有值的变量、数组元素或表达式
实参必须事先获得合法的值
不能
地址
传递
“共用存贮单元”实质依然是“遥控”
数组名
数组的地址(数组名、指向数组的指针等)
即使将形参书写成数组名(通常[]内为空),系统立即转换成相应的指针
能
“遥控”
指针
变量
变量(通常原先没有值)的地址、指针变量、数组名等
就是想通过调用函数,使实参获得值(或发生变化)
能
六、全局变量与局部变量
函数间还可以运用全局变量来交流信息。
1.定义
凡是定义在函数体外的变量称为全局变量或外部变量,其作用域从定义处开始直到程序末尾(但是也可以通过exturn拓展其作用域)。
凡是定义在函数体内的变量称为局部变量或内部变量,其作用域是本函数体内。
而在复合语句开始处定义的变量只在该复合语句中有效。
形参也是局部变量。
2.同名问题
同一个函数内的局部变量不可以同名;同一个程序中的全局变量不可以同名;全局变量与局部变量可以同名,在函数体内与局部变量同名的全局变量失效。
七、变量的存储类别和生命周期(生存期)
1、变量在内存中的存储区域
(1)静态区(从程序开始执行到整个结束)
(2)动态区(函数调用开始到被调结束)
2、存储类别
(1)auto型(自动型)
①只有局部变量(包含形参)可以定义成auto型,定义格式:
[auto]类型名变量名;
②局部变量若定义成auto型,此关键字auto可省略。
③auto型处于内存的动态区,auto型变量随所在函数被调用才被分配内存空间(活了),一旦调用完毕内存空间被收回(即释放,死了)
(2)register型(寄存器型)
少量被反复多次使用的变量一般才定义成register型。
若定义多了,系统会自动转成auto型。
除调用速度更大,其余特性同auto型。
只有局部变量(包含形参)可以定义成register型,定义格式:
register类型名变量名;
(3)static型(静态型)
①全局变量全是静态型的,且通常省略static关键字
②局部变量也可以定义成静态型,形参不可以定义成静态型。
引例:
读程题
main()
{ints,n,i;
n=100;
for(i=1;i<=n;i++)s=f(i);
printf("%d\n",s);
}
intf(intn)
{staticints=0;
s=s+n;
returns;
}
以上输出为:
5050
但若将其中的“static”改为“auto”,则输出为100。
③静态型变量如果进行初始化(即定义的同时赋予值),只有第一次执行定义语句时随着分配内存赋予初值,当退出所定义的函数时,将保留其当时值。
再次调用所在的函数时,将不重新定义,也不进行初始化,此时,该变量值是上次离开时的值。
④静态型变量如果不进行初始化,系统自动为其赋予“0”值。
3、外部变量作用域拓展声明
extern
举例:
voidfun(inta)
{externy;/*拓展声明*/
a=a*a;
printf("%d",a+y);}
inty=9;/*定义变量y*/
main()
{fun(y);}
【声明格式】
extern[原定义类型名]定义在后的全局变量;
八、递归调用
某子函数直接或间接调用自身,被称为“递归调用”。
【引例】5个小孩从大到小一次编号,问5号小孩几岁,告知比4号小2岁;
问4号,说比3号小2岁……问1号,告知10岁。
求5号的年龄。
【“栈”的特点:
“先进后出”或“后进先出”;有底“无”顶。
】
intage(intn)/*求出n号小孩年龄*/
{intnl;
if(n==1)
nl=10;
else
nl=age(n-1)-2;
returnnl;
}
main()
{printf("%d\n",age(5));
}
1、能够运用“递归”解决的问题的特点:
(1)递归的出口(1号说出年龄)
(2)有重复的操作
例:
求n!
【分析:
(1)n!
=(n-1)!
×n
(2)n=1(n=0或1)】
#include
main()
{intn;/*n>=0,n<=20*/
longJC();
FILE*fp;
if((fp=fopen("D:
\\my\\1.out","w"))==NULL)
{printf("OpenFileError\n");exit(0);}
do
scanf("%d",&n);
while(n<0||n>20);
{printf("%ld\n",JC(n));
fprintf(fp,"%ld\n",JC(n));}
fprintf(fp,"20090901007");
fclose(fp);
}
/*子函数求任意合法数的阶乘*/
longJC(intn)
{longjc;
if(n==0||n==1)
jc=1;
else
jc=JC(n-1)*n;
return(jc);
}
2、好多能用循环解决的问题都可以改用递归描述。
例如:
、进制转换等。
但是,不提倡,因为递归的时间和空间复杂度高!
九、函数的指针
函数名就是函数的首地址。
【引例】
intmax(a,b)
inta,b;
{intc;
if(a>b)c=a;
elsec=b;
return(c);
}
main()
{intx,y,z;
int(*fp)();
fp=max;
scanf("%d%d",&x,&y);
z=(*fp)(x,y);
printf("MAX=%d\n",z);
}
1、定义
所指函数的返回值类型(*指针名)();
2、说明
(1)*和指针名是一个整体,外加()提高优先级。
(2)后一个()也不能省,表示所指的是函数。
(3)定义时的“类型名”,必须和所指函数定义时的“类型名”一致,若无返回值则用void。
(4)定义后使用前,必须先赋值“函数名”(因为函数名就是函数的首地址)
(5)凡是写“函数名”的地方(除定义函数外)从此可以用“(*指针名)”替代。
十、主函数的参数
主函数(main)可以带参数,但只能是两个,且格式如下:
intargc,char**argv;
或
intargc,char*argv[];
【说明】
(1)第一个参数必须是整型,名字公认写成“argc”,但可以自定;
(2)第二个参数是指向字符串的指针数组。
名字公认写成“argv”,但可以自定;
(3)任何函数不可以调用主函数,因此实参的给定必须在DOS环境下给出。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C语言 函数 语言