第五章函数和存储类
1.函数的定义和说明
1)函数的定义:(函数的定义分为两部分,函数头和函数体。)
函数头:包含{ 函数名}和{ 参数表}以及关于该函数的{ 存储类说明}、{ 数据类型说明}、{ 参数说明}。
函数体:由花括号括起来的若干语句(一条或多条语句,复合语句,空语句)。
函数按存储种类分为:外部函数,内部函数。
外部函数前加extern关键字(常省略);
内部函数前必须加static(不能省);
注意:函数还可分为:有返回值,(在有返回值的函数定义中,除int型返回值外都必须对返回值类型进行说明);无返回值,(在无返回值的函数定义中,加不加void均可)
函数的定义不能嵌套!
举个例子:
F1(x,y)
Int x,y;
{
…
…
F2(a,b)
Int a,b;
{
Return(a+b);
}
…
…
}(像这样在f1()函数中在定义一个f2()函数的做法是错误的!)但函数的调用允许嵌套。
2)函数的说明:(有两种方法说明函数:1.简单说明(说明函数的类型);2.原型说明(说明函数和其参数的类型)。)
注意:
1.在定义函数是无说明,或者是该函数无返回值有没有加void说明符,或者是函数具有int型返回值而省略说明符时函数在调用时不用说明。
2.在定义函数时加了说明符,包含void在内,先调用后定义,以上情况调用之前必须先说明。
2.函数的参数和返回值
1)函数的参数分为:
形参(在定义函数时,参数表中在函数调用之前没有确定值的参数)
实参(在定义函数时,参数已具有确定的值。(可以是表达式))。
注意:
(1).在定义函数时,所指定的形参在该函数被调用之前是不分配内存单元的,只有在函数调用时,才给形参分配内存单元,并赋值,结束后形参所占的内存单元被释放,所以形参属于局部变量,作用范围仅在定义它的函数体内,允许一个函数的形参和实参同名,因为他们在内存中占不同的存储单元。
(2).函数调用形参或实参时,要求参数个数必须相等。
2)函数的返回值:函数的返回值都是通过return(表达式)语句来实现的。
先计算出return语句中表达式的值-->根据函数的类型对表达式的类型进行转换-->将表达式的值和理想反回给调用函数(一般会设置一个变量来接收这个返回值。)-->执行调用函数下边的语句。
举个例子:
#include <stdio.h>
float fadd(float x,float y)
{
return(x+y);
}
int main()
{
float x,y;
int sum;
printf("请输入两个小数");
scanf("%f%f",&x,&y);
sum=fadd(x,y);
printf("sum=%d\n",sum);
getchar(); getchar(); getchar();
}
结果如下:
是不是看着很不规范?我们来调一下:
#include <stdio.h>
int main()
{
float x,y;
float fadd(float a,float b );
int sum;
printf("请输入两个小数");
scanf("%f%f",&x,&y);
sum=fadd(x,y);
printf("sum=%d\n",sum);
getchar(); getchar(); getchar();
}
float fadd(float a,float b)
{
return(a+b);
}
结果如下:
3.函数的调用
函数调用基本是属于传值调用即将调用函数的实参值传递给被调用函数的形参。这个值可以是变量的值,也可以是变量的地址值。其中传值调用穿的是变量值,传址调用穿的是变量的地址值,要求实参与形参个数相等,对应的类型相同。
1.传值调用:实参使用变量名或表达式,形参使用变量名。(被调用函数的值改变不会影响调用函数的参数值)。
举个例子:
# include <stdio.h>
int main()
{
float a,b;
void f1(float x, float y);
a=7.2;
b=3.6;
f1(a,b);
printf("%.2f,%.2f\n",a,b);
getchar();getchar();getchar();
}
void f1(float x,float y)
{
x+=0.5;
y+=0.6;
printf("%.2f,%.2f\n",x,y);
}
结果为:
可以看到,被调用函数的值没有改变。
2.传址调用:在调用时传递变量地址的传值调用。(传址调用时要求调用函数的实参用地址值,二被调用函数的形参用指针,于是函数之间进行地址值的传递。)
特点:
被调用函数可已通过改变形参所指向的内容来改变调用函数的实参值。
举个例子:
# include <stdio.h>
int main()
{ void f1(int x,int *y,int *z);
int a,b,c;
a=b=c=5;
f1(a,&b,&c);
printf("a=%d,b=%d,c=%d\n",a,b,c);
getchar();getchar();getchar();
}
void f1(int x,int *y,int *z)
{
x*=2;
*y+=x;
*z=x+*y;
printf("x=%d,*y=%d,*z=%d\n",x,*y,*z);
}
结果为:
我们再来看个例子:
# include <stdio.h>
int main()
{
void f1(int *x,int *y);
void f2(int *m,int *n);
int a,b,c,d;
a=b=c=d=5;
f1(&a,&b);
f2(&c,&d);
printf("a=%d,b=%d,c=%d,d=%d\n",a,b,c,d);
getchar();getchar();
}
void f1(int *x ,int *y)
{
*x*=2;
*y+=*x;
printf("x=%d,y=%d\n",*x,*y);
}
void f2(int *m,int *n)
{
int p,q;
p=q=8;
m=&p;
n=&q;
printf("m=%d,n=%d\n",*m,*n);
}
结果为:
总结一下:
在调用函数f1()时采用传址调用方式,即实参用变量a,b和地址值&a,&b,形参用指针x,y在函数f1()中通过使用区内容运算符改变了指针x,y所指向的变量值,因此函数f1()被调用后,主函数中变量a,b的值发生了变化。
在f2()函数中也是采用传址调用方式,虽然使m和n指向了变量p和q,所以改变的也是m和n所指向的p和q的内容
下面再举几个不同参数函数调用的例子:
1)数组名做参数
要求对某一数组中的各个int型数进行由小到大的排序
# include <stdio.h>
int main()
{ void sort (int b[],int m);
static int a[8]={5,-3,10,99,78,22,6,44};
int i,n;
n=8;
sort (a,n);
for (i=0;i<8;i++)
printf("%4d\n",a[i]);
printf("\n");
getchar();getchar();
}
void sort (int b[],int m)
{
int i,j,k,temp;
for(i=0;i<m-1;i++)
k=i;
for(j=i;j<m;j++) //内重for循环
{
if(b[j]<b[k])
k=j;
temp=b[k];
b[k]=b[i];
b[i]=temp;
} //内重for循环
}
结果如下:
说一下:sort(a,n)其中实参a维一个已知数组名,实参n的值为8,可见sort()函数的两个实参中,一个是地址值(a),一个是变量值(n)。
别调用函数sort()的两个形参中,一个是数组名b,其为一个没有指定大小的一位int型数组,其大小与所对应的实参数组大小相等;另一个型参变量是m,可以看出第一个参数属于传址调用,第二个参数属于传值调用。
由于a,b数组共同占同一内存单元,因此在sort()函数中,通过b数组元素的改变,使得数组a的元素发生了变化。
(我把排序法的过程讲一下:通过双重for循环,每做一次外重for循环,从指定的数中调出最小的一个放在指定的元素位置,第二次外重循环是从剩下的7个数中再调出最小的放在第一个元素的位置,外重for循环共执行7次,将8个数按由小到大的顺序排好;内重for循环是用来确定再要查找的数中找出最小数所对应的下标值,并将它赋给k变量,然后通过三个赋值语句将数值最小的那个元素换到待选数中的最前边,即下标值为i的位置。)