C的源程序是由函数组成的,可以说函数是C程序的基本模块。除了C提供的库函数,用户可以把自己的算法编写成一个个相对独立的函数模块,然后调用该函数。
01--函数的分类
1.从函数定义看,可分为库函数和用户自定义函数
1)库函数:由C系统提供,用户无须定义,加载库函数对应的头文件即可直接调用。
比如:printf、scanf、putchar等
2)用户自定义函数:不仅需要在程序中定义函数本身,在主调函数中必须对被调函数进行类型说明
2.可以把函数分为有返回值函数和无返回值函数
1)有返回值函数:函数被调用后向调用者返回一个执行结果,该结果称之为返回值,必须在函数定义和函数说明中明确返回值的类型。
2)无返回值函数:执行完成后不向调用者返回函数值,用户在定义此类函数时可指定它的返回值为“空类型”,空类型的说明符为“void”
3.从主调函数和被调函数之间的数据传递可分为无参函数和有参函数
1)无参函数:函数定义、函数说明及函数调用中均不带参数。主调函数和被调函数之间不存在参数传递
2)有参函数:也称为带参函数。在函数定义及说明时都有参数,称之为形式参数。在函数调用时必须给出的参数称之为实际参数,在函数调用时,主调函数把实参的值传递给形参。
在一个函数体内不能再定义另一个函数,即不能嵌套定义。但是函数之间可以互相调用,函数还可以调用自己,称之为递归函数。main是主调函数,可以调用其他函数而不能被其他函数调用。一个程序必须有且只有一个主函数main
02--函数的一般形式
1.无参函数定义形式
类型标识符 函数名()
{声明部分
语句}
{}的内容为函数体,函数体中声明部分,是对函数体内部所用到的变量的类型说明
在很多情况下不要求无参函数有返回值,此时类型说明符可以写void
例:
void Soft()
{printf("How are you\n");}
Soft是一个无参函数,被调用时输出How are you字符串
2.有参函数定义的一般形式:
类型标识符 函数名(形式参数列表)
{声明部分
语句}
形式参数之间用逗号隔开,形参为变量,故在形参列表中需要给出形参的类型说明
例:
int max(int x,int y)
{
if(x>y) return x;
else return y;}
有返回值函数中至少有一个return语句。
在C程序中,一个函数的定义可以放在任意位置,可以在主函数main之前也可以在之后。
#include"stdio.h"
int max(int a,int b)
{
if(a>b)return a;
else return b;
}
int main()
{
int max(int a,int b);
int x,y,z;
printf("input two numbers:\n");
scanf("%d%d",&x,&y);
z=max(x,y);
printf("maxmum=%d",z);
return 0;
}
假如更改上述的背调函数,使它没有return语句,警告,且结果不是我们想要的
03--函数的参数和函数的值
1.实际参数和形式参数
形式参数出现在被调函数的定义中,实参出现在主调函数中,发生函数调用时,主调函数把实参的值传送给形参,从而实现主调函数向被调函数的数据传递。
1)形参变量只有在被调用时才分配内存单元,在调用结束之后就会释放所分配的内存单元。
2)实参可以是常量、变量、表达式、函数等,但在进行函数调用时,实参都必须有确定的值
3)实参和形参在数量和数据类型及顺序上必须严格一致,否则会报类型不匹配的错误
4)函数调用中数据传送是单向的,只能把实参传递给形参而不能把形参传给实参。故形参的值发生变化,但是实参的值不会变化。
#include"stdio.h"
void s(int n)
{
int i;
for(i=n-1;i>=1;i--)
n=n+i;
printf("n=%d\n",n);
}
int main()
{
int n;
printf("input number\n");
scanf("%d",&n);
s(n);
printf("n=%d\n",n);
return 0;
}
上述主函数定义了一个变量n,被调函数也定义了一个变量n
主调函数的n最后的值5需要被传递给被调函数,此时在被调函数中为变量n分配存储单元,最后经过被调函数之后n的值为15,但回到主调函数之前,被调函数中n的存储单元已经被释放,而主调函数中的n存储单元还在,故,n最终的值还是主调中定义的5
2.函数的返回值
1)函数的值只能通过return语句返回主调函数
return语句的一般形式为:
return 表达式; 或者return (表达式)
功能是计算表达式的值并返回给主调函数
2)函数值的类型和函数定义中的类型应保持一致,若不一致以函数类型为准进行自动类型转化
3)若函数值类型为整型,在函数定义时可以省去类型说明
4)不返回函数值的函数,可以定义为空类型,void
04--函数的调用
函数调用的一般形式:
函数名(实际参数列表)
1.被调函数的声明及函数原型
在主调函数中对被调函数作说明的目的是让编译系统知道被调函数返回值的类型,以便在主调函数中按此类型对返回值作处理。
一般形式为:
类型说明符 被调函数名(类型 形参1,类型 形参2,...);或为类型说明符 被调函数名(类型,类型,...);
#include"stdio.h"
long f1(int p)
{
int k;long r;long f2(int);
k=p*p;
r=f2(k);
return r;
}
long f2(int q)
{
long c=1;int i;
for(i=1;i<=q;i++)
c=c*i;
return c;
}
int main()
{
int i;
long s=0;
for (i=2;i<=3;i++)
s=s+f1(i);
printf("\ns=%ld\n",s);
return 0;
}
2.函数的递归调用
一个函数在他的函数体内调用它自身称为递归调用。
为了防止递归调用无休止的进行,必须在函数内有终止递归调用的条件
#include"stdio.h"
long ff(int n)
{
long f;
if(n<0) printf("n<0,input error");
else if(n==0||n==1) f=1;
else f=ff(n-1)*n;
return(f);
}
int main()
{
int n;
long y;
printf("\ninput a inteager number:\n");
scanf("%d",&n);
y=ff(n);
printf("%d!=%ld",n,y);
return 0;
}
05--数组作为函数参数
1.数组元素作为函数实参
#include"stdio.h"
void nzp(int v)
{
if(v>0)
printf("%d ",v);
else
printf("%d ",0);
}
int main()
{
int a[5],i;
printf("input 5 numbers\n");
for(i=0;i<5;i++)
{scanf("%d",&a[i]);
nzp(a[i]);}
return 0;
}
2.数组名作为函数参数
用数组名作函数参数时,要求形参和对应的实参都必须是类型相同的数组,都要有数组说明,不一致时会报错。在用数组名作为函数参数时,不是进行值的传送,而是进行地址的传送,数组名就是数组的首地址。形参数组名取得首地址后也就等于有了实在的数组。实际上是形参数组和实参数组为统一数组,共同拥有一段内存空间。
#include"stdio.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;
}
int main()
{
float sco[5],av;
int i;
printf("\ninput 5 scores:\n");
for(i=0;i<5;i++)
scanf("%f",&sco[i]);
av=aver(sco);
printf("average score is %5.2f",av);
return 0;
}
上式为求5个数的平均数
当用数组名作函数参数时,由于形参和实参为同一数组,因此当形参数组发生变化时,实参数组也随之变化。
#include"stdio.h"
void nzp(int a[5])
{
int i;
printf("\nvalues of array a are:\n");
for(i=0;i<5;i++)
{
if(a[i]<0) a[i]=0;
printf("%d ",a[i]);
}
}
int main()
{
int b[5],i;
printf("\ninput 5 numbers:\n");
for(i=0;i<5;i++)
scanf("%d",&b[i]);
printf("initial values of array b are:\n");
for(i=0;i<5;i++)
printf("%d ",b[i]);
nzp(b);
printf("\nlast values of array b are:\n");
for(i=0;i<5;i++)
printf("%d ",b[i]);
return 0;
}
06--局部变量和全局变量
1.局部变量
局部变量也称为内部变量,局部变量是在函数内作定义说明的。其作用域仅限于函数内,离开该函数后再使用这种变量是非法的。
1)主函数中定义的变量只能在主函数用,也不可使用其他函数定义的变量,因为主函数和其他函数是平行的关系
2)形参是被调函数的局部变量而实参是主调函数的局部变量
3)允许在不同的函数中使用相同的函数名
4)在复合语句中也可以定义变量,作用于复合语句范围内
#include"stdio.h"
int main()
{
int i=2,j=3,k;
k=i+j;
{
int k=8;
printf("%d\n",k);
}
printf("%d\n",k);
return 0;
}
2.全局变量
也称为外部变量,是函数外部定义的变量,其作用域是整个源程序
只有在函数内经过说明的全局变量才能使用,全局变量的说明符为extern
#include"stdio.h"
int s1,s2,s3;
int vs( int a,int b,int c)
{
int v;
v=a*b*c;
s1=a*b;
s2=b*c;
s3=a*c;
return v;
}
int main()
{
int v,l,w,h;
printf("\ninput length,width and height\n");
scanf("%d%d%d",&l,&w,&h);
v=vs(l,w,h);
printf("\nv=%d,s1=%d,s2=%d,s3=%d\n",v,s1,s2,s3);
return 0;
}
#include"stdio.h"
int a=3,b=5; /*a,b为外部变量*/
int max(int a,int b) /*a,b为外部变量*/
{
int c;
c=a>b?a:b;
return(c);
}
int main()
{
int a=8;
printf("%d\n",max(a,b));
return 0;
}
如果在同一个源程序中,外部变量和局部变量同名,则在局部变量的作用范围内,外部变量将不起作用
07--变量的存储类别
从变量的存在的作时间角度,变量可分为静态存储方式和动态存储方式。
静态存储:程序运行期间分配固定的存储空间
动态存储:程序运行期间根据需要进行动态的分配存储空间
全局变量全部存放在静态存储区,在程序开始执行时就分配,程序执行完成后才释放
动态存储区存放:1)函数形式参数 2)自动变量(未加static声明的局部变量)等
对以上,在函数开始调用时分配动态存储空间,函数调用结束时释放这些空间
1.auto变量
函数中的局部变量,如果不专门声明为static存储类别,都是动态的分配存储空间的,函数的形参和函数定义的变量(包括复合语句中定义的变量),都属于此类,这类局部变量都属于自动变量。自动变量用关键字auto作存储类别的声明。关键字auto可以省略。
2.static声明局部变量
有时希望函数中的局部变量的值在函数调用结束后不释放而保留原值,这是应该指定局部变量为静态局部变量,用关键字static进行声明
#include"stdio.h"
int f(int a)
{auto int b=0;
static int c=3;
b=b+1;
c=c+1;
return(a+b+c);
}
int main()
{int a=2,i;
for(i=0;i<3;i++)
printf("%d\n",f(a));
return 0;
}
上式中c的值为静态局部变量,c的值存储的空间不会被释放,但是b的值的存储区间被释放了
故最后的结果为
#include"stdio.h"
int fac(int n)
{static int f=1;
f=f*n;
return(f);
}
int main()
{int i;
for(i=1;i<=5;i++)
printf("%d!=%d\n",i,fac(i));
return 0;
}
3.寄存器变量 register
通常变量的值是存放在内存中,当对⼀个变量频繁读写时,则需要反复访问内存储器,从⽽花费⼤量的存取时间。为了提⾼效率,C语⾔提供了另⼀种变量,即寄存器变量
1)寄存器变量的分配是动态完成的,因此只有局部变量和形式参数才能定义为寄存器变量。
2)局部静态变量不能定义为寄存器变量,因为一个变量只能声明为一种存储类别,不能同时使用 static 和 register。
3)寄存器的长度通常与机器的字长一致,只有较短的类型如 int、char、short 等才适合定义为寄存器变量,不推荐将 double 等较大的类型定义为寄存器类型。
4)CPU 的寄存器数目有限,即使定义了寄存器变量,编译器可能并不真正为其分配寄存器,而是将其当做普通的 auto 变量来对待,为其分配栈内存。
#include"stdio.h"
int fac(int n)
{register int i,f=1;
for(i=1;i<=n;i++)
f=f*i;
return f;
}
int main()
{int i;
for(i=0;i<=5;i++)
printf("%d!=%d\n",i,fac(i));
return 0;
}
本程序循环6次,变量 i 都将随着循环被频繁地调⽤。为了提⾼程序运⾏效率,所以定义这个变量为寄存器变量。
4.用extern声明外部变量
外部变量(全局变量)是在函数外部定义的,其作用域为从变量的定义处开始,到该程序的文件末尾结束。如果在定义点之前的函数想要引用该外部变量,则应该在引用之前用关键字extern对该变量作外部变量声明。
#include"stdio.h"
int max(int x,int y)
{int z;
z=x>y?x:y;
return(z);
}
int main()
{extern int A,B;
printf("%d\n",max(A,B));
return 0;
}
int A=13,B=-8;