第六章 函数
- 6.1 函数概述
- 定义
- 函数:是具有一定功能的一个程序块;是C语言的基本组成单位。
- 在前面各章的例子及读者自己编写的C语言程序中都用到了以“main“开头的主函数,并且在程序中频繁地调用了C语言提供的用于输入输出的库函数( scanf( )和printf( )函数)。
- 函数是C源程序的基本模块,通过对函数模块的调用实现特定的功能。
- 一个C程序可由一个主函数和若干个其他函数构成,并且只能有一个主函数。由主函数来调用其他函数,其他子函数之间也可以互相调用。
- C程序的执行总是从main()函数开始。调用其他函数完毕后,程序流程回到main( )函数,继续执行主函数中的其他语句,直到main( )函数结束,则整个程序的运行结束。
- main( )函数是由系统定义的。所有的函数都是平行的,即在函数定义时它们是互相独立的,函数之间并不存在从属关系。也就是说,函数不能嵌套定义(这是与PASCAL不同的),函数之间可以互相调用,但不允许调用main( )函数。
- 分类
- 从用户的使用的角度看,函数有两种:
- (1) 标准函数,即库函数。这些函数由系统提供,可以直接使用。
- (2) 自定义的函数。用以解决用户需要时设计定义的函数。
- 从函数的形式看,函数分为两类:
- (1)无参函数。
- (2)有参函数。
- 从用户的使用的角度看,函数有两种:
- 定义
- 6.2 函数定义的一般形式
- 函数的定义
- C语言中函数定义的一般形式如下:
- 函数返回值的类型名 函数名(类型名 形式参数1,类型名 形式参数2, … )
- {
- 说明部分;
- 语句部分;
- }
- C语言中函数定义的一般形式如下:
- 说明:
- 函数名和各个形式参数都是由用户命名的合法标识符,与普通变量名的定义规则相同。在同一程序中,函数名必须唯一,不能出现重名的情况。形式参数名只要在同一函数中唯一即可,由于形式参数的作用域不相同,因此形式参数名可以与其他函数中的变量名同名。C语言规定,不能在一个函数内部再定义函数,也就是说函数不能嵌套定义。
- 若在函数的首部省略了函数返回值的类型名,可以把函数首部写成:
- 函数名(类型名形式参数1 ,类型名 形式参数2 ,…,类型名 形式参数n)
- 紧跟在函数名之后的圆括号中的内容是形式参数和类型说明表,在每个形参之前都要有类型名,以标识形式参数的类型。各形参的定义之间用逗号分隔。
- 例如,求两整数和的函数:
- int add(int a ,int b)
- {
- intt; /* 函数体中声明部分 */
- t=a+b;
- return t;
- }
- 例如,求两整数和的函数:
- 若所定义的函数没有形参,函数名后的一对圆括号依然不能省略。本例中函数体中的语句是用来完成求和的功能。在某些情况下,函数体可以是空的,例如:
- fun()
- { }
- 该函数中没有任何语句,什么工作也不做,没有任何实际作用。之所以要在主调函数上这样写,是为了表明此处要调用一个函数,而现在这个函数的具体功能可能还没有设计好,没有起作用,等以后扩充函数功能时补充上即可。
- 在函数体中,除形参外,用到的其他变量必须在说明部分进行定义,这些变量(包括形参)只在函数被调用时才被临时分配内存单元,当退出函数时,这些临时开辟的存储单元全部被释放掉,即在该函数体内部定义的变量都将不存在。因此,这些变量只在函数体内部起作用,与其他函数体内的变量并不相关。
- 函数的定义
- 6.3 函数参数和函数返回值
- 6.3.1 形式参数和实际参数
- 定义
- 在程序中调用函数时,绝大多数情况下,主调函数和被调函数之间会发生数据传递关系,这就要用到前面提到的有参函数。在定义函数时,函数名后面括号中的变量称为“形式参数“(简称“形参“);在主调函数中,函数名后面括号中的参数(可以是一个表达式)称为“实际参数“(简称“实参“)。
- 说明:
- (1)实参可以是常量、变量或表达式。
- (2)在被定义的函数中必须指定形参类型。
- (3)实参与形参的类型应相同或赋值相兼容。
- (4)C语言规定,实参变量对形参变量的数据传递是“值传递“,即单向传递。只能由实参传给形参,而不能由形参返回来给实参。在内存中,实参单元与形参单元是不同的单元。
- (5)在调用函数时,给形参分配存储单元,并将实参对应的值传递给形参。调用结束后,形参单元被释放,实参单元仍保留并维持原值。
- (6)一定要注意参数之间的传递,实参和形参之间 传数值,和传地址的差别。传数值的话,形参的变化不会改变实参的变化。传地址的话,形参的变化就会有可能改变实参的变化。
- 定义
- 6.3.2 函数的返回值
- 函数的返回值就是通过函数调用使主调函数能得到一个确定的值。函数的值通过return语句返回,return语句的形式如下:
- return 表达式;
- 或return(表达式);
- 或return;
- return 语句中的表达式的值就是所求的函数值。此表达式值的类型必须与函数首部所说明的类型一致。若类型不一致,则以函数值的类型为准,由系统自动进行转换。
- 例如 通过函数调用的方法求1到自然数n(n>1)自然数的和,有程序段如下
- #include<stdio.h>
- ints(int n)
- {
- int i,sum=0;
- for(i=1;i<=n;i++)
- sum+=i;
- return sum;
- }
- main()
- {
- int n;
- printf("input number ");
- scanf("%d",&n);
- n=s(n);
- printf("1到n的和为:%d ",n);
- }
- 例如 通过函数调用的方法求1到自然数n(n>1)自然数的和,有程序段如下
- 函数的返回值就是通过函数调用使主调函数能得到一个确定的值。函数的值通过return语句返回,return语句的形式如下:
- 6.3.1 形式参数和实际参数
- 6.4 函数的调用
- 6.4.1 函数调用的一般形式
- 函数名(实参表列);
- 函数的调用可以分为调用无参函数和调用有参函数两种,如果是调用无参函数,则不用“实参表列“,但括号不能省略。在调用有参函数时,若实参列表中有多个实参,各参数间用逗号隔开。实参与形参要求类型一致。
- 6.4.2 函数调用的方式
- (1)函数语句。把函数调用做为一个语句,这时该函数只需要完成一定的操作而不必有返回值。
- (2)函数表达式。当一个函数出现在一个表达式中,该表达式就被称为函数表达式。因为要参与表达式中的计算,所以要求该函数有一个确定的返回值提供给表达式。
- (3)函数参数。函数调用做为一个函数的实参。
- 6.4.3 C语言中,调用函数和被调用函数之间的数据可通过3种方式进行传递。
- (1)实参与形参之间进行数据传递。
- (2)通过return语句把函数值返回到主调用函数中。
- (3)通过全局变量。
- 6.4.3 函数的递归调用
- 函数的递归调用一定要记得有结束的条件
- 在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。允许函数的递归调用是C语言的特点之一。
- 当一个问题在采用递归法解决时,必须符合以下3个条件:
- (1)可以把要解决的问题转化为一个新的问题。而这个新的问题的解决方法仍与原来的解决方法相同,只是所处理的对象有规律地递增或递减。
- (2)可以应用这个转化过程使问题得到解决。
- (3)必须要有一个明确的结束递归的条件。
- 当函数自己调用自己时,系统将自动把函数中当前的变量和形参暂时保留起来,在新一轮的调用过程中,系统将为本次调用的函数所用到的变量和形参,开辟新的存储单元。因此,递归调用的层次越多,同名变量所占的存储单元也就越多。当本次调用的函数运行结束时,系统将释放本次调用所占的存储单元。当程序执行的流程返回到上一层的调用点时,同时取用进入该层函数中的变量和形参所占用的存储单元中的数据。
- 例如 求n!的值,有程序段如下:
- #include<stdio.h>
- longff(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("请输入整数值: ");
- scanf("%d",&n);
- y=ff(n);
- printf("%d!=%ld",n,y);
- }
- 6.4.1 函数调用的一般形式
- 6.5 函数的说明
- 6.5.1 形式
- 概念
- C语言中,除了主函数外,对于用户定义的函数要遵循先定义后使用的规则。把函数的定义放在调用之后,应该在调用之前对函数进行说明(或函数原型说明)。
- 函数说明的一般形式如下:
- 类型名 函数名(参数类型1 ,参数类型2 ,…,参数类型n);
- 如
- intadd(int ,int );
- 如
- 或
- 类型名 函数名(参数类型1 参数名1,参数类型2 参数名2 ,…,参数类型n 参数名n);
- 此处的参数名完全是虚设的,它们可以是任意的用户标识符,既不必与函数首部中的形参名一致,又可以与程序中的任意用户标识符同名,实际上参数名常常省略。函数说明语句中的类型名必须与函数返回值的类型一致。
- 类型名 函数名(参数类型1 ,参数类型2 ,…,参数类型n);
- 函数说明可以是一条独立的语句。对函数进行说明,能使C语言的编译程序在编译时进行有效的类型检查。当调用函数时,若实参的类型与形参的类型不能赋值兼容而进行非法转换,C编译程序将会发现错误并报错;当实参的个数与形参的个数不同时,编译程序也将报错。
- 概念
- 6.5.2函数说明的位置
- 一个函数在所有函数的外部,如在被调用之前说明,则在说明后的所有位置上都可以对该函数进行调用。如在main()函数内部进行说明,则只能在main()函数内部才能识别。
- 例如 调用求和函数输出1到n的和值,程序段如下:
- #include<stdio.h>
- main()
- {
- int n;
- int s(int n);
- printf("input number ");
- scanf("%d",&n);
- s(n);
- printf("n=%d ",n);
- }
- ints(int n)
- {
- int i,sum=0;
- for(i=1;i<=n;i++)
- sum+=i;
- printf("1到n的和值为:%d ",n);
- }
- 一定要有:函数名,函数的返回类型,函数的参数类型。不一定要有:形参的名称。
- 6.5.1 形式
- 6.6 常用函数
- 6.6.1 字符串 <string.h>
- 6.6.1.1 strlen 计算长度
- 6.6.1.2 strcmp 比较
- 6.6.1.3 strcpy 复制 (不安全,越界)
- 6.6.1.4 strcat 追加 (不安全,越界)
- 6.6.2 数学函数<math.h>
- 6.6.2.1 向上,向下取整
- 函数名: ceil
- 功能:向上取整
- 用法: double ceil(double x);
- 函数名: floor
- 功能:向下取整
- 用法: double floor(double x);
- 函数名: ceil
- 6.6.2.2 取绝对值
- 函数名:abs
- 功能:返回整型数的绝对值.
- 用法:abs(number)
- number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回Null;如果是未初始化变量,则返回 0.
- 函数名:fabs
- 功能:求浮点数x的绝对值.
- 用法:fabs (double x);
- 函数名:abs
- 6.6.2.3 平方根
- 函数名:sqrt
- 功能:返回指定数字的平方根.
- 用法:sqrt (double x);
- 函数名:sqrt
- 6.6.2.4 求幂
- 函数名:exp
- 功能:返回 e 的 n 次幂.
- 用法:exp (double x);
- 函数名:pow
- 功能:返回指定数字的指定次幂.
- 用法:pow (double x, double y);(将返回x的y次幂)
- 函数名:exp
- 6.6.2.5 取余
- 函数名: fmod
- 功 能: 计算x对y的模, 即x/y的余数
- 用 法:double fmod(double x, double y);
- 函数名: fmod
- 6.6.2.6 对数
- 函数名:log
- 功能: 自然对数函数ln(x)
- 用法: double log(double x);
- 函数名:log10
- 功能:返回以 10 为底的对数.
- 用法:log10(double x);
- 函数名:log
- 6.6.2.7 三角函数:(所有参数必须为弧度)
- sin
- 函数声明:sin (double x);
- 用途:用来返回给定的 X 的正弦值。
- cos
- 函数声明:cos (double x);
- 用途:用来返回给定的 X 的余弦值。
- tan
- 函数声明:tan (double x);
- 用途:用来返回给定的 X 的正切值。
- sin
- 6.6.2.8 反三角函数
- acos
- 函数申明:acos (double x);
- 用途:用来返回给定的 X 的反余弦函数。
- asin
- 函数申明:asin (double x);
- 用途:用来返回给定的 X 的反正弦函数。
- atan
- 函数申明:atan (double x);
- 用途:用来返回给定的 X 的反正切函数。
- atan2
- 函数声明:atan2 (double y, double x);
- 用途:返回给定的 X 及 Y 坐标值的反正切值
- acos
- 6.6.2.9 双曲函数
- 函数名:cosh
- 功能:返回指定角度的双曲余弦值.
- 用法:Double Cosh(double x(以弧度计量的角度)) ;
- 函数名:sinh
- 功能:返回指定角度的双曲正弦值。
- 用法:sinh (double x);(其中参数x必须为弧度制)
- 函数名:tanh
- 功能:回指定角度的双曲正切值.
- 用法:tanh (double x);
- 函数名:cosh
- 6.6.2.10 实型数分整数和小数
- 函数名:modf
- 功 能: 把数分为整数部分和小数部分
- 用 法: double modf(doublevalue, double *iptr);
- eg
- 1. #include<math.h>
- 2.
- 3. #include<stdio.h>
- 4.
- 5. int main(void)
- 6.
- 7. {
- 8.
- 9. double fraction,integer;
- 10.
- 11. double number =100000.567;
- 12.
- 13. fraction =modf(number, &integer);
- 14.
- 15. printf("Thewhole and fractional parts of %lf are %lf and %lf ",
- 16.
- 17. number, integer,fraction);
- 18.
- 19. return 0;
- 20.
- 21. }
- The whole andfractional parts of 100000.567000 are 100000.000000 and 0.567000
- 函数名:modf
- 6.6.2.11 随机数
- 在编程的时候需要电脑来获取一些随机的反应,这个时候我们可以使用随机数,比较常见的是 rand() 函数,它可以随机的产生 0 ~rand_max 的随机数。rand_max 是一个很大的数字,具体关系到IDE和数据类型,我们一般的需要不可能超出它的范围
- C语言中还有一个 random() 函数可以获取随机数,但是 random() 函数不是ANSI C标准,不能在VC等编译器通过,所以比较少用。
- eg
- int a=rand()%10; //产生0~9的随机数,注意10会被整除
- int a=rand()%51+13; //产生13~63的随机数
- 产生 13~63 范围内随机数的完整代码:
- 1. #include <stdio.h>
- 2. #include <stdlib.h>
- 3. #include <time.h>
- 4. int main(){
- 5. int a;
- 6. srand((unsigned)time(NULL));
- 7. a=rand()%51+13;
- 8. printf("%d ",a);
- 9. return 0;
- 10. }
- 下面是一个实例:
- 1. #include <stdio.h>
- 2. #include <stdlib.h>
- 3. int main(){
- 4. int a=rand();
- 5. printf("%d ",a);
- 6. return 0;
- 7. }
- 编译后再运行几次,你会发现产生的随机数是相同的。实际上,rand() 函数产生的随机数是伪随机数,是根据一个数按照某个公式推算出来的,这个数我们称之为“种子”,但是这个种子在系统启动之后就是一个定值,我们需要用 srand() 来进行播种,即在int a前加一句:
- 1. srand((unsigned)time(NULL)); //这里利用时间进行播种,需要time.h
- sqrt( ) fabs( ) pow( ) sin( )
- 6.6.2.1 向上,向下取整
- 6.6.3 malloc
- malloc的返回类型是 void *
- int *p;
- p = (int *)malloc(2);
- p = (int *)malloc(sizeof(int));以上两个等价
- 6.6.1 字符串 <string.h>