C专栏C-函数知识

 

/*************************************************

function:c language Technology
author  :chinayaosir   QQ:44633197 
blog    :http://blog.csdn.net/chinayaosir
note    :禁止其它网站转载此文章

**************************************************/

 

C源程序是由函数组成,所以也把C语言称为函数式语言
函数的主要知识点(函数声明.定义.调用.参数传递.函数主体.返回值.库函数)

******目录******
1.函数分类
1.1库函数/用户函数
.库函数 (由C系统提供的函数)
.用户函数(由用户自己定义的函数)

1.2有参/无参函数
.有参函数(主调函数向被调函数传送数据)
.无参函数(主调函数与被调函数间无数据传送)

1.3有/无返回值函数
.有返回值的函数(向调用者返回函数值,应说明函数类型( 即返回值的类型 )
.无返回值的函数(不返回函数值,说明为空(void)类型)

1.4内部/外部函数
.内部函数(只能在本源文件中使用的函数)
.外部函数(可在整个源程序中使用的函数)

2.函数声明与定义
2.1函数声明 [extern] 类型声明符 函数名([形参表]);
2.2函数定义 [extern/static] 类型说明符 函数名([形参表]) 方括号内为可选项。

3. 函数参数
3.1一般参数(发生函数调用时,将把实参的值传送给形参,实参修改,形参保持不变)
3.2数组参数(数组不进行值传送而进行地址传送,因此形参值发生变化,实参值当然也变化)
3.3指针参数(指针不进行值传送而进行地址传送,因此形参值发生变化,实参值当然也变化)

4. 函数调用
4.1调用格式 函数名([实参表])
4.2调用方式 (嵌套调用和递归调用)

5. 函数变量分类(1.变量的数据类型,2.变量作用域,3.变量的存储类型)
5.1变量的数据类型
5.2变量作用域(变量有效范围,分为局部变量和全局变量。
5.3变量存储方式(自动变量 auto,寄存器变量register,外部变量extern,静态全局变量static)
--------------------------------------------------------------------------------------
******知识内容******

--------------------------------------------------------------------------------------
1.1库函数/用户函数
.库函数  
由C系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。
在前面各章的例题中反复用到printf 、 scanf 、 getchar 、putchar、gets、puts、strcat等函数均属此类。
C语言提供了极为丰富的库函数
(1)字符类型分类函数   用于对字符按ASCII码分类:字母,数字,控制字符,分隔符,大小写字母等。
(2)转换函数    用于字符或字符串的转换;在字符量和各类数字量 (整型,实型等)之间进行转换;在大、小写之间进行转换。
(3)目录路径函数   用于文件目录和路径操作。
(4)诊断函数    用于内部错误检测。
(5)图形函数    用于屏幕管理和各种图形功能。
(6)输入输出函数   用于完成输入输出功能。
(7)接口函数    用于与DOS,BIOS和硬件的接口。
(8)字符串函数    用于字符串操作和处理。
(9)内存管理函数   用于内存管理。
(10)数学函数   用于数学函数计算。
(11)日期和时间函数   用于日期,时间转换操作。
(12)进程控制函数   用于进程管理和控制。
(13)其它函数   用于其它各种功能。
应首先掌握一些最基本、 最常用的函数,再逐步深入。

.用户函数
由用户按需要写的函数。对于用户自定义函数,不仅要在程序中定义函数本身,
而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。

----------------------------------------------------------------------------
1.2有参/无参函数

一、函数的参数
  前面已经介绍过,函数的参数分为形参和实参两种。形参和实参具有以下特点:
1.形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。
因此,形参只有在函数内部有效。 函数调用结束返回主调函数后则不能再使用该形参变量。
2.实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,
它们都必须具有确定的值,以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。
3.实参和形参在数量上,类型上,顺序上应严格一致,否则会发生“类型不匹配”的错误。
4.函数调用中发生的数据传送是单向的。 即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。
因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。
void main()
{
int n;
printf("input number/n");
scanf("%d",&n);
s(n);
printf("n=%d/n",n);
}
int s(int n)
{
int i;
for(i=n-1;i>=1;i--)
n=n+i;
printf("n=%d/n",n);
}
----------------------------------------------------------------------------
1.3有/无返回值函数
.函数的值
1. 函数的值只能通过return语句返回主调函数。
return 语句的一般形式为:
return 表达式; 或者为:return (表达式);
该语句的功能是计算表达式的值,并返回给主调函数。

2.有返回值函数就是函数体中有return (表达式);
3.无返回值函数就是函数体没有return 并且函数返回类型void或者没有定义;

----------------------------------------------------------------------------
1.4内部/外部函数
函数一旦定义后就可被其它函数调用。
但当一个源程序由多个源文件组成时,在一个源文件中定义的函数能否被其它源文件中的函数调用呢?
为此,C语言又把函数分为两类:
.内部函数
如果在一个源文件中定义的函数只能被本文件中的函数调用,
而不能被同一源程序其它文件中的函数调用,这种函数称为内部函数。
定义内部函数形式: static 类型说明符 函数名(形参表)
例如:static int f(int a,int b) 内部函数也称为静态函数。
但此处静态static 的含义已不是指存储方式,而是指对函数的调用范围只局限于本文件。
因此在不同的源文件中定义同名的静态函数不会引起混淆。

.外部函数(extern)
外部函数在整个源程序中都有效,其定义的一般形式为: extern 类型说明符 函数名(形参表)
例如:extern int f(int a,int b)如在函数定义中没有说明extern或static则隐含为extern。
在一个源文件的函数中调用其它源文件中定义的外部函数时,应用extern说明被调函数为外部函数。
例如:
main(){
extern int f1(int i); /*外部函数说明,表示f1函
数在其它源文件中*/
……
}
F2.C (源文件二)
extern int f1(int i); /*外部函数定义*/
{
……
}
----------------------------------------------------------------------------

2.函数声明与定义
2.1函数声明 [extern] 类型声明符 函数名([形参表])

其一般形式为: 类型声明符 被调函数名(类型,类型…);
现代格式的括号内给出了形参的类型和形参名,或只给出形参类型。

若用现代格式可写为:
int max(int a,int b);或写为:int max(int,int);

不用函数声明的情况
.如果被调函数的返回值是整型或字符型时,可以不对被调函数作说明,而直接调用
.当被调函数的函数定义出现在主调函数之前时,在主调函数中也可以不对被调函数再作说明而直接调用。
**函数的定义放在main 函数之前,因此可在main函数中省去对 max函数的函数说明int max(int a,int b)。
.如在所有函数定义之前,在函数外预先说明了各个函数的类型,则在以后的各主调函数中,可不再对被调函数作说明。
.对库函数的调用不需要再作说明,但必须把该函数的头文件用include命令包含在源文件前部。

2.2函数定义 [extern/static] 类型说明符 函数名([形参表]) 方括号内为可选项。
函数定义就是用C代码实现一个完整的函数源码
----------------------------------------------------------------------------
3. 函数参数
3.1一般参数(发生函数调用时,将把实参的值传送给形参,实参修改,形参保持不变)
3.2数组参数(数组不进行值传送而进行地址传送,因此形参值发生变化,实参值当然也变化)
数组用作函数参数有两种形式
------------------------------------
.数组元素(下标变量)作为实参使用;
数组元素作函数实参数组元素就是下标变量,它与普通变量并无区别。
因此它作为函数实参使用与普通变量是完全相同的,在发生函数调用时,把作为实参的数组元素的值传送给形参,实现单向的值传送。
例5.4说明了这种情况。[例5.4]判别一个整数数组中各元素的值,若大于0 则输出该值,若小于等于0则输出0值。
void nzp(int v)
{
if(v>0)
printf("%d ",v);
else
printf("%d ",0);
}
main(){
int a[5],i;
printf("input 5 numbers/n");
for(i=0;i<5;i++)
{
scanf("%d",&a[i]);
nzp(a[i]);
}
}void nzp(int v)
{ ……}
main(){
int a[5],i;
printf("input 5 numbers/n");
for(i=0;i<5;i++)
{ scanf("%d",&a[i]);
nzp(a[i]);
}
}
------------------------------------
.数组名作为函数的形参和实参使用。
用数组名作函数参数与用数组元素作实参有几点不同:
用数组元素作实参时,只要数组类型和函数的形参变量的类型一致,
那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的。因此,并不要求函数的形参也是下标变量。
在普通变量或下标变量作函数参数时,形参变量和实参变量是由编译系统分配的两个不同的内存单元。
数组a中存放了一个学生5门课程的成绩,求平均成绩。float aver(float a[5])
改用数组名作函数参数。
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]);
}
}
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]);
}
void nzp(int a[5])
{ ……
}
main()
{
int b[5],i;
……
nzp(b);
……
}

3.3指针参数(指针不进行值传送而进行地址传送,因此形参值发生变化,实参值当然也变化)

----------------------------------------------------------------------------
4. 函数调用
递归调用:函数还可以自己调用自己。
嵌套调用:函数之间允许相互调用,也允许嵌套调用
C程序的执行总是从main函数开始,完成对其它函数的调用后再返回到main函数,最后由main函数结束整个程序。

4.1调用格式 函数名([实参表])
4.2调用方式 (嵌套调用和递归调用)
一个函数在它的函数体内调用它自身称为递归调用。 这种函数称为递归函数。C语言允许函数的递归调用。
为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。
常用的办法是加条件判断,满足某种条件后就不再作递归调用,然后逐层返回。
下面举例说明递归调用的执行过程。
[例5.9]用递归法计算n!用递归法计算n!可用下述公式表示:
n!=1 (n=0,1)
n×(n-1)! (n>1)
按公式可编程如下:
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);
}
main()
{
int n;
long y;
printf("/ninput a inteager number:/n");
scanf("%d",&n);
y=ff(n);
printf("%d!=%ld",n,y);
}
long ff(int n)
{ ……
else f=ff(n-1)*n;
……
}
main()
{ ……
y=ff(n);
……
}
程序中给出的函数ff是一个递归函数。
主函数调用ff 后即进入函数ff执行,如果n<0,n==0或n=1时都将结束函数的执行,否则就递归调用ff函数自身。
由于每次递归调用的实参为n-1,即把n-1 的值赋予形参n,最后当n-1的值为1时再作递归调用,形参n的值也为1,将使递归终止。
然后可逐层退回。下面我们再举例说明该过程。 设执行本程序时输入为5,即求 5!。
在主函数中的调用语句即为y=ff(5),进入ff函数后,由于n=5,不等于0或1,故应执行f=ff(n-1)*n,即f=ff(5-1)*5。
该语句对ff作递归调用即ff(4)。 逐次递归展开如图5.3所示。
进行四次递归调用后,ff函数形参取得的值变为1,故不再继续递归调用而开始逐层返回主调函数。
ff(1)的函数返回值为1,ff(2)的返回值为1*2=2,ff(3)的返回值为2*3=6,ff(4) 的返
回值为6*4=24,最后返回值ff(5)为24*5=120。
----------------------------------------------------------------------------
5. 函数变量分类(1.变量的数据类型,2.变量作用域,3.变量的存储类型)
5.1变量的数据类型(基本类型和高级类型)
  .基本类型(整数int,数值float,double,字符char,布尔boolean,空null)
  .高级类型=数组,结构,指针,共用,枚举)
5.2变量作用域(分为局部变量和全局变量)
.局部变量
局部变量也称为内部变量,局部变量是在函数内作定义说明的。其作用域仅限于函数内,离开该函数后再使用这种变量是非法的。
例如:
int f1(int a) /*函数f1*/
{int b,c;
……
}a,b,c作用域
int f2(int x) /*函数f2*/
{
int y,z;
}x,y,z作用域
main()
{
int m,n;
}
m,n作用域 在函数f1内定义了三个变量,a为形参,b,c为一般变量。
在 f1的范围内a,b,c有效,或者说a,b,c变量的作用域限于f1内。
同理,x,y,z的作用域限于f2内。
m,n的作用域限于main函数内。
关于局部变量的作用域还要说明以下几点:

1. 主函数中定义的变量也只能在主函数中使用,不能在其它函数中使用。
同时,主函数中也不能使用其它函数中定义的变量。因为主函数也是一个函数,它与其它函数是平行关系。
这一点是与其它语言不同的,应予以注意。
2. 形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。
3. 允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。
如在例5.3 中,形参和实参的变量名都为n,是完全允许的。
4. 在复合语句中也可定义变量,其作用域只在复合语句范围内。
----------------------------------------------------------------------------
.全局变量
全局变量也称为外部变量,它是在函数外部定义的变量。 它不属于哪一个函数,它属于一个源程序文件。
其作用域是整个源程序。在函数中使用全局变量,一般应作全局变量说明。 只有在函数内经过说明的全局变量才能使用。
全局变量的说明符为extern。 但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明。
例如:int a,b; /*外部变量*/
void f1() /*函数f1*/{……}
float x,y; /*外部变量*/
int fz() /*函数fz*/
{……}
main() /*主函数*/
{……}
/*全局变量x,y作用域 全局变量a,b作用域*/
从上例可以看出a、b、x、y 都是在函数外部定义的外部变量,都是全局变量。
但x,y 定义在函数f1之后,而在f1内又无对x,y的说明,所以它们在f1内无效。
a,b定义在源程序最前面,因此在f1,f2及main内不加说明也可使用。
因此外部变量是实现函数之间数据通讯的有效手段。对于全局变量还有以下几点说明:

1. 对于局部变量的定义和说明,可以不加区分。而对于外部变量则不然,外部变量的定义和外部变量的说明并不是一回事。
外部变量定义必须在所有的函数之外,且只能定义一次。
其一般形式为: [extern] 类型说明符 变量名,变量名… 其中方括号内的extern可以省去不写。
例如: int a,b;等效于:extern int a,b;
而外部变量说明出现在要使用该外部变量的各个函数内,在整个程序内,可能出现多次,
外部变量说明的一般形式为: extern 类型说明符 变量名,变量名,…;
外部变量在定义时就已分配了内存单元,外部变量定义可作初始赋值,外部变量说明不能再赋初始值,只是表明在函数内要使用某外部变量。
2. 外部变量可加强函数模块之间的数据联系,但是又使函数要依赖这些变量,因而使得函数的独立性降低。
从模块化程序设计的观点来看这是不利的,因此在不必要时尽量不要使用全局变量。
3. 在同一源文件中,允许全局变量和局部变量同名。在局部变量的作用域内,全局变量不起作用。
[例5.13]int vs(int l,int w)
{
extern int h;
int v;
v=l*w*h;
return v;
}
main()
{
extern int w,h;
int l=5;
printf("v=%d",vs(l,w));
}
int l=3,w=4,h=5;
  本例程序中,外部变量在最后定义,因此在前面函数中对要用的外部变量必须进行说明。
外部变量l,w和vs函数的形参l,w同名。外部变量都作了初始赋值,mian函数中也对l作了初始化赋值。
执行程序时,在printf语句中调用vs函数,实参l的值应为main中定义的l值,等于5,外
部变量l在main内不起作用;实参w的值为外部变量w的值为4,进入vs后这两个值传送给形参l,wvs函数中使用的h 为外部变量,其值为5,
因此v的计算结果为100,返回主函数后输出。变量的存储类型各种变量的作用域不同,就其本质来说是因变量的存储类型相同。
所谓存储类型是指变量占用内存空间的方式,也称为存储方式。


----------------------------------------------------------------------------
5.3变量存储方式(自动变量 auto,寄存器变量register,外部变量extern,静态全局变量static)
.自动变量auto。
这种存储类型是C语言程序中使用最广泛的一种类型。
C语言规定,函数内凡未加存储类型说明的变量均视为自动变量,也就是说自动变量可省去说明符auto。

.特点:
.自动变量的作用域仅限于定义该变量的个体内。
 函数中定义的自动变量,只在该函数内有效,在复合语句中定义的自动变量只在该复合语句中有效。
.因此函数调用结束之后,自动变量的值不能保留,
 在使用它,即定义该变量的函数被调用时才给它分配存储单元,开始它的生存期。
 函数调用结束,释放存储单元,结束生存期。
.由于自动变量的作用域和生存期都局限于定义它的个体内( 函数或复合语句内),因此不同的个体中允许使用同名的变量而不会混淆。
 即使在函数内定义的自动变量也可与该函数内部的复合语句中定义的自动变量同名。
. 对构造类型的自动变量如数组等,不可作初始化赋值。
-----------------------------------
.外部变量extern。
外部变量的几个特点:
1. 当一个源程序由若干个源文件组成时,在一个源文件中定义的外部变量在其它的源文件中也有效。
2. 在一个源文件中定义extern变量,则在另一个源文件可以使用这extern个变量。

在前面介绍全局变量时已介绍过外部变量。
这里再补充说明
例如有一个源程序由源文件F1.C和F2.C组成: F1.C
int a,b; /*外部变量定义*/
char c; /*外部变量定义*/
main()
{
……
}
F2.C
extern int a,b; /*外部变量说明*/
extern char c; /*外部变量说明*/
func (int x,y)
{
……
}
在F1.C和F2.C两个文件中都要使用a,b,c三个变量。在F1.C文件中把a,b,c都定义为外部变量。
在F2.C文件中用extern把三个变量说明为外部变量,表示这些变量已在其它文件中定义,
并把这些变量的类型和变量名,编译系统不再为它们分配内存空间。
对构造类型的外部变量,如数组等可以在说明时作初始化赋值,若不赋初值,则系统自动定义它们的初值为0。
-----------------------------------
.静态变量static
静态局部变量具有以下特点:
(1)静态局部变量在函数内定义,变量始终存在着,它的生存期为整个源程序,不象自动变量那样,当调用时存在,退出函数时消失。
(2)静态局部变量的生存期虽然为整个源程序,但是其作用域仍与自动变量相同,即只能在定义该变量的函数内使用该变量。
(3)允许对构造类静态局部量赋初值。若未赋以初值,则由系统自动赋以0值。
(4)对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。
根据静态局部变量的特点,可以看出它是一种生存期为整个源程序的量。
1. 静态局部变量  在局部变量的说明前再加上static说明符就构成静态局部变量。
例如:
static int a,b;
static float array[5]={1,2,3,4,5};
虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时,
它又可继续使用,而且保存了前次被调用后留下的值。
因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。
虽然用全局变量也可以达到上述目的,但全局变量有时会造成意外的副作用,因此仍以采用局部静态变量为宜。
程序如下:
main(){
int i;
void f();
for (i=1;i<=5;i++)
f();
}
void f(){
static int j=0;
++j;
printf("%d/n",j);
}
void f(){
static int j=0;
++j;
printf("%d/n",j);
}
由于j为静态变量,能在每次调用后保留其值并在下一次调用时继续使用,所以输出值成为累加的结果。
-----------------------------------
.静态全局变量(static)
静态全局变量是在全局变量(外部变量)的说明之前再冠以static
由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。

从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。
把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
因此static 这个说明符在不同的地方所起的作用是不同的。应予以注意。
-----------------------------------
.寄存器变量(register)
register变量存放在CPU的寄存器中
1.使用时,不需要访问内存,而直接从寄存器中读写,这样可提高效率。
2.寄存器变量使用要求点:
.只有局部自动变量和形式参数才可以定义为寄存器变量。
 因为寄存器变量属于动态存储方式,凡需要采用静态存储方式的量不能定义为寄存器变量。
.在Turbo C,MS C等微机上使用的C语言中,实际上是把寄存器变量当成自动变量处理的。
.即使能真正使用寄存器变量的机器,由于CPU 中寄存器的个数是有限的,因此使用寄存器变量的个数也是有限的。
 对于循环次数较多的循环控制变量及循环体内反复使用的变量均可定义为寄存器变量。
3.code sample
求∑200i=1i
main()
{
register i,s=0;
for(i=1;i<=200;i++)
s=s+i;
printf("s=%d/n",s);
}
----------------------------------------------------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值