760 likes | 881 Views
第一章 函数与宏定义. 1.1 函数概念. 1.2 变量作用域和存储类型. 1.3 内部函数与外部函数. 1.4 递归函数设计和调用. 1.5 预处理. 1.6 综合范例. 1.1 函数概念. 函数是 C 程序 的基本组成单位 , 每个 C 程序由一个主函数 main() 和任意个函数组成。 每个函数对应特定的功能。意义 :. ( 1 ) 便于设计复杂的大程序 。. ( 2 ) 便于程序的维护和扩充。. main ( ). a. b. c. . x. z. d.
E N D
第一章 函数与宏定义 1.1 函数概念 1.2 变量作用域和存储类型 1.3 内部函数与外部函数 1.4 递归函数设计和调用 1.5 预处理 1.6 综合范例
1.1函数概念 函数是C程序的基本组成单位,每个C程序由一个主函数main()和任意个函数组成。每个函数对应特定的功能。意义: (1) 便于设计复杂的大程序。 (2) 便于程序的维护和扩充。
main ( ) a b c x z d C程序由一个main和任意个函数组成。 1)程序从main开始执行,最后又回到main函数结束。 2)由主函数调用其他函数,其他函数也可以互相调用,且同一函数可以被一个或多个函数调用任意次。 3) 除main外, 其它为标准库函数、用户自定义函数, 标准库函数:由系统提供,用户可调用。 用户自定义函数:由用户按语法规则编写。
库函数简介 系统自带的标准库函数根据不同的功能作用放在不同的头文件中。 stdio.h 用于标准输入/输出 math.h 用于数学计算 ctype.h 用于字符处理 string.h 用于字串处理 time.h 用于时间/日期的处理 graphics.h 用于图形操作 库函数的使用提高了编程效率。使用时只需在程序的开头加上一条语句: #include <.h> 根据使用的函数来确定
函数体 1.1.1 函数定义 [存储类型符] [返回值类型符] 函数名([形参说明表]) { 说明部分 执行部分 }
例 1: 定义函数hello,在屏幕上输出“Hello World!” hello () { printf(“Hello World!\n”); } 例 2: 定义函数add,实现整数的加法运算 add (int x, int y) { int result; result=x+y; printf(“%d”,result); } int add (int x, int y) { int result; result=x+y; return result; }
函数返回语句的形式有以下两种: 1、函数无返回值的情况 return;或 根本没有return语句 2、函数有返回值的情况 return (表达式); 或 return 表达式; 例 3: 定义函数max,找出两个整数中的最大值并返回 int max (int x, int y) { int m; m=x>y? x : y ; return m; }
1.1.2 函数声明和调用 一、 函数的声明 调用函数前应对被调用函数进行声明(说明),目的是告诉编译器所要使用的函数的信息,便于编译器对函数调用作精确的类型检查。 函数声明的一般形式: [存储类型符][返回值类型符] 函数名( [形参说明表]); 如: int max(int x,int y);
函数声明 例:求二实数之和(1-1.c)。 main ( ) { float add(float x, float y ); float a, b, c; scanf("%f, %f",&a, &b); c=add(a, b); printf ("sum= %f";c); } float add( float x, float y) { float z; z=x+y; return z; } 函数调用 函数定义 例1、例2 当函数定义在函数调用之前时,可省略对被调函数的声明。
二、 调用方式 (1)无返回值的情况 函数名([实参列表]); (2)有返回值的情况 变量名=函数名([实参列表]);
单向 实参值 形参。 传递 1.1.3 函数的传值方式 函数参数 实参: 出现在函数调用中的参数。 形参: 出现在函数定义中的参数。 临时分配单元给形参 调用时: 调用完毕,释放形参单元。
例:(1-2.c) int max (int x, int y) {int z; z=x>y? x:y; return (z); } main( ) {int a, b, c; scanf("%d, %d", &a, &b); c=max(a, b); printf("max=%d\n", c); }
例1-2.c函数调用中参数传递过程为: 在main函数中 int a, b, c; a 10 scanf("%d, %d", &a, &b); b 15 c 15 c=max(a, b); int max(int x, int y) { int z; x 10 z=x>y? x:y; y 15 15 z return(z); }
注意(函数参数): 形参变量在被调用前不占用存储单元;在调用结束后,形参所占存储单元亦被释放。 实参可以是常量、变量或表达式。 必须指定形参类型,且必须与实参的类型一致。 实参对形参的数据传递是“传值方式”。即单向传递,不能逆传。
例:阅读下列程序,写出运行结果(1-3.c) swap(int x, int y) {int t; a 15 t=x; x=y; y=t; b 19 printf("x=%d, y=%d\n", x, y);} main( ) { int a, b; scanf("%d, %d", &a, &b); x 15 19 y 15 if (a<b) swap(a, b); 19 15 t printf("a=%d, b=%d\n", a, b); }
关于main函数的参数: 一般情况下,main函数均无参数,但这并不表示main 函数必须为无参函数,在必要时,main函数也可以带有参数。
实参传递给形参时,实参的计算有的系统自左至右,有的自右至左。实参传递给形参时,实参的计算有的系统自左至右,有的自右至左。 例 int f(int a,int b) { int c; if(a>b) c=1; else if(a==b) c=0; else c=-1; return(c); } main() { int i=2,p; p=f(i,++i); printf("%d",p); } Turbo C是按自右至左的顺序求实参表的值,所以上机运运行结果为0。
注意(函数返回值): • 一个函数中可以有一个以上的return语句,执行到哪一个return语句,则哪一个语句起作用。 如:#include "stdio.h“ fun(int x, int n); main( ) { int n=4, x=2, s; s=fun(x, n); printf("s=%d\n", s); } fun(int x, int n) { if (n==1) return(x); else return(xfun(x, n1)); }
如果函数定义中没有给出函数值类型,一律按int型处理。如果函数定义中没有给出函数值类型,一律按int型处理。 如:float max(float x, float y) 函数值为float型 char letter(char c1, char c2) 函数值为字符型 f(int a, int b) 函数值为int型
当函数值类型与return语句中表达式类型不一致时,以函数值类型为准。对数值型数据,可以自动进行类型转换。即函数类型决定返回值的类型。1-2.c int z->float z (这种方法初学者最好不用) • 若被调函数中没有return语句,将不带回一个确定的、用户希望的函数值。但实际上,并非不带回值,而只是不带回有用的值,带回的是一个不确定的值。
如:(1-4.c) printstar( ) {printf("************\n"); } print_message( ) {printf(" How do you do!\n");} main ( ) {int a, b, c; a=printstar( ); b=print_message( ); c=printstar( ); printf("a=%d, b=%d, c=%d\n", a, b, c); } }
为了明确表示不需要函数返回值,可以用“void”定义函数为“无类型”。此时,不得使用a=printstar( )之类的语句。 练习: • 编写计算圆面积的函数并调用(1-5.c)。 • 编写由三角形三条边求面积的函数并调用(1-5-0.c)。 • 打印出1000以内的所有素数,要求用到函数yes(n),其功能为判断某数n是否为素数(1-9.c)。 • 计算1+(1+2)+(1+2+3)+…+(1+2+3+…+n) 要求(1)n由键盘输入 (2)每项的计算由函数完成(1-10.c)。
main { 调用a } a( ) { 调用b } b( ) { } 1.1.4 函数互相调用 主函数调用其他函数,其他函数也可以互相调用。 函数不能嵌套定义,但可嵌套调用
例:编写求组合数的函数。(1-6.c) 组合数的计算公式为: → 从程序中可以看到: (1)在定义函数时,函数fac、comb是互相独立的; (2)二个函数的定义均在主函数之前,所以在主函数中不必再对它们进行声明(说明);
main函数 comb函数 fac函数 调用comb函数 调用fac函数 输出c (3)程序从主函数main()开始执行,首先执行的scanf( ),输入数,然后调用comb(n,m),调用comb的过程中,要调用fac( )。fac( )的调用是嵌套在函数comb( )的调用中进行的 ,其嵌套调用过程如下:
例1:任何一个整数n的立方都可以表示为n个相邻奇数之和。其中最大奇数为d=2m-1,而m=1+2+3+…+n。 (1-7-0.c) 算法: • 从键盘输入整数n; • 求m=1+2+3+…+n的值; • 求d=2m-1的值; • 求所有奇数d, d-2, d-4, …, d-2*(n-1)。 例2:定义函数max(a,b,c),功能为求三个整型数中的最大值。利用该函数,编程求五个数的最大数(1-7.c) 。
1.2变量作用域及存储类型 一、变量作用域 根据变量的有效作用范围,变量可分为局部变量和全局变量。 1、局部变量 凡在函数(含main 函数)内部定义的变量称为局部变量。 局部性: 局部变量仅在函数内部有效,它包括:
1. 函数中定义的变量; 不同的函数可具有同名的变量, 它们占不同的内存单元, 互不影响。 2. 形式参数; 3. 在复合语句中可定义仅复合语句中有效的临时变量。(1-11.c )
2、全局变量 一个源文件中,在所有函数之外定义的变量为全局变量。 有效性:自定义位置开始至文件结尾全部有效。
例: int p=1, q=5; float f1(int a) {int b, c; } char c1, c2; p,q的作用范围 char f2(int x,int y); { int i, j; } main ( ) } } c1, c2的作用范围
1. 全局变量可以增强各函数间数据的联系。同一文件中的所有函数都能引用全局变量的值。当一个函数对其值进行改变后,另一个函数使用该变量的值亦相应改变。好处: 函数之间值传递。 2.不要随意使用全局变量。一是始终占据内存单元;二是由于函数依赖于外部定义的变量,降低了通用性。 3.不在全局变量作用域内的函数。若要使用全局(外)变量,需在函数体内加上extern保留字予以说明。 4.全局和局部变量同名时,局部变量有效。
例: float f1 (int x) {extern int a, b; } int a=0; b= –1 main ( ) { } a, b作用域
int m=13; • int fun(int x, int y) • {int m=3; • printf("m1=%d\n",m); • return(x*y-m); } • main() • {int a=6,b=7; • printf("m2=%d\n",m); • printf("%d\n",fun(a,b)/m); } x, y m 全局变量m的作用范围 a, b 例:分析m作为全局变量和局部变量在程序中各个部分时的值。(1-12.c )
程序区 静态存储区 动态存储区 内存分配 数据,变量存放 二、 变量的存储类型 1、变量的存储类型 说明变量占用存储空间的区域。
静态存储变量: 存放于静态存储区,在程序整个运行过程中,始终占据固定的内存单元。 动态存储变量: 存放于动态存储区,根据程序的运行状态(如:函数调用)而临时分配的单元,且单元并不固定。
常用的变量存储类型有三种: 1.自动 (auto)型 2.静态(static)型 3.外部(extern)型
2、局部变量存储形式 局部变量既可以静态方式 , 又可以动态方式存储。 动态方式: auto int a, b; 则: a, b为自动型,存入动态区。在该函数被调用时才分配单元,函数调用结束时释放。 auto一般省略。以前用到的变量均为auto型, (除加static说明的之外)。 在变量初始化方面,auto型变量在每次函数调用时都赋一次初值。其默认初值不确定。 例:auto型变量的初始化 (1-13-0.c )
静态方式: static int a, b; 则: a, b存入静态区。 函数中的a, b始终占据固定存储单元。 如果希望在函数调用结束后仍然保留函数中的局部变量的值,则可以把该变量定义为静态局部变量。 在变量初始化方面,静态局部变量是在第一次调用函数时赋初值的,且只赋一次初值。默认初值为0。 例:static局部变量的初始化 (1-13-0.c ) 例:求n! (1-13.c )
2、全局变量存储形式 在函数外部中定义,它们一定存放在静态存贮区中。 全局变量既可被本文件中各函数用,亦可被其它源文件中的函数引用。 (1)只被本文件中的函数引用 全局变量本身一定是存放在静态区的。但若加上 static. 即:
static int a, b; float f1(x) int { } 则表明a,b只被本文件中各函数引用,即使与其它文件中的全局变量同名,也互不影响。
(2)可被其它文件中的函数引用(外部类型,默认)(2)可被其它文件中的函数引用(外部类型,默认) extern int a; fac(x) int x { z=a } int a; main( ) { } 文件f2.c用到f1.c 中的a 文件f1.c f2.c中的extern在函数外说明,在函数内说明已叙述过。
函数内 函数外 存储类别 存在性 作用域 作用域 存在性 auto static 局部 (只限本文件) static 全局 不加static 全局(外部) 总结 例:静态局部变量的作用域与存在性 (1-13-1.c )
1.3内部函数与外部函数 函数本身在一个文件中为全局的。即函数可被其所在文件的所有其它函数引用。 但函数能否被其它文件中的函数所引用呢? 为此分为: 内部函数、外部函数 一、内部函数–––只能在本文件中调用 static 类型标识符 函数名(形参表)
例: static int max (int a, int b) { } 则该函数max只能被本文件中的其它函数引用,而不能被其它文件中的函数引用。即如果在不同的文件中有同名的内部函数,将互不干扰。
二、外部函数 既可被本文件中的函数调用,也可被其它文件中的函数调用。 extern 类型标识符 函数名(形参表) extern 可省略 一般系统在调用外部函数的函数中用extern说明外部函数。
例: (1-14.c 1-15.c ) /*file2.c*/ extern int add(int m, int n) { return (m+n); } /*file1.c*/ #include<stdio.h> int mod(int a, int b); extern int add(int m, int n); main() { int x,y,result; scanf(“%d%d”,&x,&y); result=add(x,y); if (result>0) result=result-mod(x,y); printf(“result=%d\n”,result); } int mod(int a,int b) { return (a%d); }
由多个源文件组成一个程序: 1. 命令方式。 2. 建立项目文件。 3. 使用文件包含命令。
首先要生成两个源文件(file1.c 和file2.c)。再构造project文件,在编辑状态下,编辑一个后缀为prj的文件(文件名可由用户选择,如:ff.prj)此文件内容如下: file1 file2 后缀.c可有可无,file1,file2顺序无关,如果是file1.c和file2.c不在一个目录中时,可在project文件ff.prj中给出各自的路径。 选择project功能项,在project name输入ff.prj。 然后按F9,产生相应的执行文件ff.exe 。
1.4 递归函数设计和调用 递归调用: 在函数内调用函数本身,称为函数的递归调用。函数直接调用本身,称为直接递归。函数调用其他函数 ,其他函数又调用了本函数,称为间接递归。
直接调用 int f(int x) { int y, z; z=f (y); } 间接调用 int f1 (int x) { int y, z; z=f2 (y); } int f2 (int t) { int a, b; a=f1 (y); } 以上仅给出了递归的概念.