1.前导程序
//9.1.0函数 #include<stdio.h> #define NAME "GIGATHINK,INC." #define ADDRESS "101 Megapolis" #define PLACE "Megapolis,CA 94904" #define WIDTH 40 void starbar(void);//函数声明 int main(void) { starbar();//函数调用 printf("%s ",NAME); printf("%s ",ADDRESS); printf("%s ",PLACE); starbar(); return 0; } void starbar(void) { int count; for(count=1;count<=WIDTH;count++) putchar('*'); putchar(' '); }
2.函数概述
- 函数是用于完成特定任务的代码的自包含单元。一般而言函数可同时具有,执行动作和返回值两个操作。
- 如果把函数写在了另一个单独的文件中,则在那个文件中必须加入#define和#include指令。
- 一个程序分析
//打印一个信头 #include<stdio.h> #include<string.h> #define NAME "GIGATHINK,INC." #define ADDRESS "101 Megabuck Plaza" #define PLACE "Megapolis,CA 94904" #define WIDTH 40 #define SPACE ' ' void show_n_char(char ch,int num);//函数定义 int main(void) { int spaces; show_n_char('*',WIDTH);//使用常量作为参数 putchar(' '); show_n_char(SPACE,12); printf("%s ",NAME); spaces=(WIDTH-strlen(ADDRESS))/2;//让程序计算 //需要跳过多个空格 show_n_char(SPACE,spaces); //用一个变量作为参数 printf("%s ",ADDRESS); show_n_char(SPACE,(WIDTH-strlen(PLACE))/2);//用一个表达式作为参数 printf("%s ",PLACE); show_n_char('*',WIDTH); putchar(' '); return 0; } void show_n_char(char ch,int num) { int count; for(count=1;count<=num;count++) putchar(ch); }
(1)void show_n_char (char ch,int num)这行代码通知编译器shpw_n_char()函数使用名为ch和num的两个参数,并且这两个参数的类型分别为char和int类型,变量ch和num被称为形式参量(数),他们是局部变量时函数所私有的,这意味着在其他函数中,可以使用相同的变量名,每当调用函数时,这些变量就会被赋值。
(2)ANSI C形式要求在每个变量前声明类型。如void dibs(int x,y,z)这样的函数头是不正确的,可以写成void dibs(x,y,z) int x,y,z;这样的形式。
(3)在原型中使用变量名并没有实际的创建变量,这些变量名只是虚设的名字,不必和函数定义中的使用的变量名相匹配。
(4)show_n_char(SPACE,12)。实际参数是空格和12,这两个数值被赋给show_n_char()中相应的形式参数:变量ch和num;实际参数可以是常量变量或者是一个更复杂的表达式,但是无论何种形式的实际参量,计算时都要首先计算其值,然后将该值复制给被调用函数中相应的形式参数。不管在被调用函数中对复制数值进行什么操作,调用函数中的原数值不会受到任何影响。
(5)黑盒子观点:黑盒子内的一切操作对调用函数来说是不可见的。
(6)函数应该进行类型声明,其类型应该和返回值类型相同。函数声明要在函数使用之前,它只是将函数类型告诉编译器,而函数定义部分则是函数的实际实现代码。
(7)return :给函数返回一个值或者是终止执行函数。
//找出两个整数中的较小者的类型是int所以imin函数的类型也是int //返回变量min的值 #include<stdio.h> int imin(int,int); int main(void) { int evil1,evil2; printf("Enter a pair of integers(q to quit): "); while(scanf("%d%d",&evil1,&evil2)==2) { printf("The lesser of %d and %d is %d. ",evil1,evil2,imin(evil1,evil2));//函数调用imin(evil1,evil2)只是复制了两个变量的值 printf("Enter a pair of integers(q to quit): "); } printf("Bye. "); return 0; } int imin(int n,int m) { int min; if(n<m) min=n; else min=m; return min; }
//不正确的使用函数 //程序仍然可以编译通过 #include<stdio.h> int imax(); int main(void) { printf("The maximum of %d and %d is %d. ",3,5,imax(3));//漏掉了一个参数,第二个则去堆栈中的其它值 printf("The maximum of %d and %d is %d. ",3,5,imax(3.0,5.0));//使用浮点参数而不是整型参数 return 0; } int imax(n,m) int n,m;//请注意变量声明的位置 { int max; if(n>m) max=n; else max=m; return max; } /* #include<stdio.h> int imax(int,int);//a int main(void) { printf("The maximum of %d and %d is %d. ",3,5,imax(3,5));//b printf("The maximum of %d and %d is %d. ",3,5,imax(3.0,5.0));//调用时被转换成了调用imax(3,5) return 0; } int imax(int n,int m)//c { int max; if(n>m) max=n; else max=m; return max; } */
3.递归
//递归举例 #include<stdio.h> void up_and_down(int); int main(void) { up_and_down(1); return 0; } void up_and_down(int n) { printf("Level %d:n location %p ",n,&n);//1 if(n<4) up_and_down(n+1); printf("Level %d:n location %p ",n,&n);//2 }
- 一个函数调用其本身,这种调用过程被称为递归。
- 递归的基本原理
- 每一级的函数调用都有自己的变量。
- 每一次函数调用都会有一次返回。当程序执行到某一级递归的结尾处时它会转移到前第一级递归继续执行,程序不能直接返回main()中的初始调用部分,而是通过递归的每一级逐步返回。
- 递归函数中,位于递归调用前的语句和各级被调用函数具有相同的执行顺序。
- 位于递归调用后的语句的执行顺序和各个被调用函数的顺序相反。
- 虽然每一级递归都有自己的变量,但是函数代码并不会得到复制。
- 递归函数中必须包含可以终止递归调用的语句。
- 递归和反向计算(#以二进制形式输出整数)
//以二进制形式输出整数 #include<stdio.h> void to_binary(unsigned long n) { int r; r=n%2; if(n>=2) to_binary(n/2); putchar(r?'1':'0'); return ; } int main(void) { unsigned long number; printf("Enter an integer(q to quit): "); while(scanf("%lu",&number)==1) { printf("Binary equivalent:"); to_binary(number); putchar(' '); printf("Enter an integer(q to quit): "); } printf("Done. "); return 0; }
- 尾递归:把递归调用语句放在函数结尾即恰在return语句之前。
//使用循环和递归计算阶乘 #include<stdio.h> long fact(int n); long rfact(int n); int main(void) { int num; printf("This program calculates factorials. "); printf("Enter a value in the rang 0-12(q to quit): "); while(scanf("%d",&num)==1) { if(num<0) printf("No negative number,please. "); else if(num>12) printf("keep input under 13. "); else { printf("loop:%d factorial=%ld ",num,fact(num)); printf("recursion:%d factorial = %ld. ",num,rfact(num)); } printf("Enter a value in the rang 0-12(q to quit): "); } printf("Bye. "); return 0; } long fact(int n)//使用循环计算阶乘 { long ans; for(ans=1;n>1;n--) ans*=n; return ans; } long rfact(int n)//使用递归调用阶乘 { long ans; if(n>0) ans=n*rfact(n-1); else ans=1; return ans; }
4.地址运算符:&
- 一个变量的地址可以被看做是该变量在内存中的位置。
//查看变量的存储地址 #include<stdio.h> void mikado(int);//函数声明 int main(void) { int pooh=2,bah=5;//main()函数中的局部变量 printf("In main(),pooh=%d and &pooh=%p ",pooh,&pooh); printf("In main(),bah=%d and &bah=%p ",bah,&bah); mikado(pooh); return 0; } void mikado(int bah)//定义函数 { int pooh=10;//函数中的局部变量 printf("In mikado(),pooh=%d and &pooh=%p ",pooh,&pooh); printf("In mikado(),bah=%d and &bah=%p ",bah,&bah); } //计算机会把它们看作是4个独立的变量 //关于数值传递和数址传递
5.指针简介
(1)指针是一个其数值为地址的变量。指针变量的数值表示的是地址。
(2)ptr=&pooh;//把pooh的地址赋给ptr。ptr=&bah;//令ptr指向bah而不是pooh。
(3)指针是一种新的数据类型,而不是整数类型。
1 nurse=22; 2 int*ptr;//指针声明 3 ptr=&nurse;//指向nurse的指针 4 val=*ptr;将ptr指向的值赋给val,即val=22;
(4)通常情况下,可以把关于变量的两类信息传递给参数,分别是参数的值和参数的地址。
(5)float*pi;//pi是一个指针,而且*pi是float类型的。
//交换函数 /* #include<stdio.h> void interchange(int u,int v); int main(void) { int x=5,y=10; printf("Originally x=%d and y=%d. ",x,y); interchange(x,y); printf("Now x=%d and y=%d. ",x,y); return 0; } void interchange(int u,int v) { int temp; temp=u; u=v; v=temp; } //数据交换失败 */ //交换函数@使用指针 #include<stdio.h> void interchange(int *u,int* v);//1 int main(void) { int x=5,y=10; printf("Originally x=%d and y=%d. ",x,y); interchange(&x,&y);//参数是两个地址2 printf("Now x=%d and y=%d. ",x,y); return 0; } void interchange(int *u,int *v)//3 { int temp; temp=*u; *u=*v; *v=temp; }