第7章 用函數實現模塊化程序設計
輸出以下的結果,用函數調用實現。
******************
How do you do!
******************
#include <stdio.h>
int main()
{ void print_star();
void print_message();
print_star();
print_message();
print_star();
return 0;
}
void print_star()
{ printf(“******************\n”); }
void print_message()
{ printf(“ How do you do!\n”); }
為什么要用函數
說明:
1、一個C程序由一個或多個程序模塊組成,每一個程序模塊作為一個源程序文件。對較大的程序,一般不希望把所有內容全放在一個文件中,而是將它們分別放在若干個源文件中,由若干個源程序文件組成一個C程序。這樣便於分別編寫、分別編譯,提高調試效率。一個源程序文件可以為多個C程序共用。
2、一個源程序文件由一個或多個函數以及其他有關內容(如預處理指令、數據聲明與定義等)組成。一個源程序文件是一個編譯單位,在程序編譯時是以源程序文件為單位進行編譯的,而不是以函數為單位進行編譯的。
3、C程序的執行是從main函數開始的,如果在main函數中調用其他函數,在調用后流程返回到main函數,在main函數中結束整個程序的運行。
4、所有函數都是平行的,即在定義函數時是分別進行的,是互相獨立的。一個函數並不從屬於另一個函數,即函數不能嵌套定義。函數間可以互相調用,但不能調用main函數。main函數是被操作系統調用的。
5、從用戶使用的角度看,函數有兩種。
庫函數,它是由系統提供的,用戶不必自己定義而直接使用它們。應該說明,不同的C語言編譯系統提供的庫函數的數量和功能會有一些不同,當然許多基本的函數是共同的。
用戶自己定義的函數。它是用以解決用戶專門需要的函數。
6、從函數的形式看,函數分兩類。
無參函數。無參函數一般用來執行指定的一組操作。無參函數可以帶回或不帶回函數值,但一般以不帶回函數值的居多。
有參函數。在調用函數時,主調函數在調用被調用函數時,通過參數向被調用函數傳遞數據,一般情況下,執行被調用函數時會得到一個函數值,供主調函數使用。
C語言要求,在程序中用到的所有函數,必須“先定義,后使用”
指定函數名字、函數返回值類型、函數實現的功能以及參數的個數與類型,將這些信息通知編譯系統。
輸入兩個整數,要求輸出其中值較大者。要求用函數來找到大數。
int max(int x,int y)
{
int z;
z=x>y?x:y;
return(z);
}
————————————————————————————————————————
#include <stdio.h>
int main()
{ int max(int x,int y);
int a,b,c;
printf(“two integer numbers: ");
scanf(“%d,%d”,&a,&b);
c=max(a,b);
printf(“max is %d\n”,c);
}
int max(int x, int y)
{
int z;
z=x>y? x:y;
return(z);
}
輸入兩個實數,用一個函數求出它們之和。
#include <stdio.h>
int main()
{ float add(float x, float y);
float a,b,c;
printf("Please enter a and b:");
scanf("%f,%f",&a,&b);
c=add(a,b);
printf("sum is %f\n",c);
return 0;
}
float add(float x,float y)
{ float z;
z=x+y;
return(z);
}
輸入4個整數,找出其中最大的數。用函數的嵌套調用來處理。
#include <stdio.h>
int main()
{ int max4(int a,int b,int c,int d);
int a,b,c,d,max;
printf(“4 interger numbers:");
scanf("%d%d%d%d",&a,&b,&c,&d);
max=max4(a,b,c,d);
printf("max=%d \n",max);
return 0;
}
int max4(int a,int b,int c,int d)
{ int max2(int a,int b);
int m;
m=max2(a,b);
m=max2(m,c);
m=max2(m,d);
return(m);
}
int max2(int a,int b)
{ if(a>=b) return a;
else return b;
}
函數的遞歸調用
在調用一個函數的過程中又出現直接或間接地調用該函數本身,稱為函數的遞歸調用。
C語言的特點之一就在於允許函數的遞歸調用。
例7.6 有5個學生坐在一起
問第5個學生多少歲?他說比第4個學生大2歲
問第4個學生歲數,他說比第3個學生大2歲
問第3個學生,又說比第2個學生大2歲
問第2個學生,說比第1個學生大2歲
最后問第1個學生,他說是10歲
請問第5個學生多大
#include <stdio.h>
int main()
{
int age(int n);
printf("NO.5,age:%d\n",age(5));
return 0;
}
int age(int n)
{
int c;
if(n==1)
c=10;
else
c=age(n-1)+2;
return(c);
}
用遞歸方法求n!。
#include <stdio.h>
int main()
{ int fac(int n);
int n; int y;
printf("input an integer number:");
scanf("%d",&n);
y=fac(n);
printf("%d!=%d\n",n,y);
return 0;
}
int fac(int n)
{ int f;
if(n<0)
printf("n<0,data error!");
else if(n==0 | | n==1)
f=1;
else f=fac(n-1)*n;
return(f);
}
Hanoi(漢諾)塔問題。古代有一個梵塔,塔內有3個座A、B、C,開始時A座上有64個盤子,盤子大小不等,大的在下,小的在上。有一個老和尚想把這64個盤子從A座移到C座,但規定每次只允許移動一個盤,且在移動過程中在3個座上都始終保持大盤在下,小盤在上。在移動過程中可以利用B座。要求編程序輸出移動一盤子的步驟。
思路:
要把64個盤子從A座移動到C座,需要移動大約264 次盤子。一般人是不可能直接確定移動盤子的每一個具體步驟的
老和尚會這樣想:假如有另外一個和尚能有辦法將上面63個盤子從一個座移到另一座。那么,問題就解決了。此時老和尚只需這樣做:
(1) 命令第2個和尚將63個盤子從A座移到B座
(2) 自己將1個盤子(最底下的、最大的盤子)從A座移到C座
(3) 再命令第2個和尚將63個盤子從B座移到C座
將n個盤子從A座移到C座可以分解為以下3個步驟:
(1) 將A上n-1個盤借助C座先移到B座上
(2) 把A座上剩下的一個盤移到C座上
(3) 將n-1個盤從B座借助於A座移到C座上
編寫程序。
用hanoi函數實現第1類操作(即模擬小和尚的任務)
用move函數實現第2類操作(模擬大和尚自己移盤)
函數調用hanoi(n,one,two.three)表示將n個盤子從“one”座移到“three”座的過程(借助“two”座)
函數調用move(x,y)表示將1個盤子從x 座移到y 座的過程。x和y是代表A、B、C座之一,根據每次不同情況分別取A、B、C代入
#include <stdio.h>
int main()
{ void hanoi(int n,char one, char two,char three);
int m;
printf(“the number of diskes:");
scanf("%d",&m);
printf("move %d diskes:\n",m);
hanoi(m,'A','B','C');
}
void hanoi(int n, char one, char two, char three)
{ void move(char x,char y);
if(n==1)
move(one,three);
else
{ hanoi(n-1,one,three,two);
move(one,three);
hanoi(n-1,two,one,three);
}
}
void move(char x,char y)
{
printf("%c-->%c\n",x,y);
}
有一個一維數組score,內放10個學生成績,求平均成績。
#include <stdio.h>
int main()
{ float average(float array[10]);
float score[10], aver; int i;
printf("input 10 scores:\n");
for(i=0;i<10;i++)
scanf("%f",&score[i]);
printf("\n");
aver=average(score);
printf("%5.2f\n",aver);
return 0;
}
float average(float array[10])
{ int i;
float aver,sum=array[0];
for(i=1;i<10;i++)
sum=sum+array[i];
aver=sum/10;
return(aver);
}
有兩個班級,分別有35名和30名學生,調用一個average函數,分別求這兩個班的學生的平均成績。
#include <stdio.h>
int main()
{ float average(float array[ ],int n);
float score1[5]={98.5,97,91.5,60,55};
float score2[10]={67.5,89.5,99,69.5,77,89.5,76.5,54,60,99.5};
printf(“%6.2f\n”,average(score1,5));
printf(“%6.2f\n”,average(score2,10));
return 0;
}
float average(float array[ ],int n)
{ int i;
float aver,sum=array[0];
for(i=1;i<n;i++)
sum=sum+array[i];
aver=sum/n;
return(aver);
}
用選擇法對數組中10個整數按由小到大排序。
#include <stdio.h>
int main()
{ void sort(int array[],int n);
int a[10],i;
printf("enter array:\n");
for(i=0;i<10;i++) scanf("%d",&a[i]);
sort(a,10);
printf("The sorted array:\n");
for(i=0;i<10;i++) printf("%d ",a[i]);
printf("\n");
return 0;
}
void sort(int array[],int n)
{ int i,j,k,t;
for(i=0;i<n-1;i++) // i取值0-8
{ k=i;
for(j=i+1;j<n; j++)
if(array[j]<array[k]) k=j;
t=array[k];
array[k]=array[i];
array[i]=t;
}
}
有一個3×4的矩陣,求所有元素中的最大值。
#include <stdio.h>
int main()
{ int max_value(int array[][4]);
int a[3][4]={{1,3,5,7},{2,4,6,8},{15,17,34,12}};
printf(“Max value is %d\n”, max_value(a));
return 0;
}
int max_value(int array[][4])
{ int i,j,max;
max = array[0][0];
for (i=0; i<3; i++)
for(j=0; j<4; j++)
if (array[i][j]>max) max = array[i][j];
return (max);
}
局部變量和全局變量
定義變量可能有三種情況:
在函數的開頭定義
在函數內的復合語句內定義
在函數的外部定義
局部變量
在一個函數內部定義的變量只在本函數范圍內有效
在復合語句內定義的變量只在本復合語句范圍內有效
在函數內部或復合語句內部定義的變量稱為“局部變量”
全局變量
在函數內定義的變量是局部變量,而在函數之外定義的變量稱為外部變量
外部變量是全局變量(也稱全程變量)
全局變量可以為本文件中其他函數所共用
有效范圍為從定義變量的位置開始到本源文件結束
有一個一維數組,內放10個學生成績,寫一個函數,當主函數調用此函數后,能求出平均分、最高分和最低分。
#include <stdio.h>
float Max=0,Min=0;
int main()
{ float average(float array[ ],int n);
float ave,score[10]; int i;
printf("Please enter 10 scores:\n");
for(i=0;i<10;i++)
scanf("%f",&score[i]);
ave=average(score,10);
printf("max=%6.2f\nmin=%6.2f\n
average=%6.2f\n",Max,Min,ave);
return 0;
}
float average(float array[ ],int n)
{ int i; float aver,sum=array[0];
Max=Min=array[0];
for(i=1;i<n;i++)
{ if(array[i]>Max) Max=array[i];
else if(array[i]<Min) Min=array[i];
sum=sum+array[i];
}
aver=sum/n;
return(aver);
}
動態存儲方式與靜態存儲方式
從變量值存在的時間(即生存期)觀察,變量的存儲有兩種不同的方式:靜態存儲方式和動態存儲方式
靜態存儲方式是指在程序運行期間由系統分配固定的存儲空間的方式
動態存儲方式是在程序運行期間根據需要進行動態的分配存儲空間的方式
-
自動變量(auto變量)
局部變量,如果不專門聲明存儲類別,都是動態地分配存儲空間的
調用函數時,系統會給局部變量分配存儲空間,調用結束時就自動釋放空間。因此這類局部變量稱為自動變量
自動變量用關鍵字auto作存儲類別的聲明
int f(int a)
{
auto int b,c=3;
┇
}
-
靜態局部變量(static局部變量)
希望函數中的局部變量在函數調用結束后不消失而繼續保留原值,即其占用的存儲單元不釋放,在下一次再調用該函數時,該變量已有值(就是上一次函數調用結束時的值),這時就應該指定該局部變量為“靜態局部變量”,用關鍵字static進行聲明
例7.16 考察靜態局部變量的值。
#include <stdio.h>
int main()
{ int f(int);
int a=2,i;
for(i=0;i<3;i++)
printf(“%d\n”,f(a));
return 0;
}
int f(int a)
{ auto int b=0;
static c=3;
b=b+1;
c=c+1;
return(a+b+c);
}
每調用一次,開辟新a和b,但c不是
輸出1到5的階乘值。
#include <stdio.h>
int main()
{ int fac(int n);
int i;
for(i=1;i<=5;i++)
printf(“%d!=%d\n”,i,fac(i));
return 0;
}
int fac(int n)
{ static int f=1;
f=f*n;
return(f);
}
若非必要,不要多用靜態局部變量
-
寄存器變量(register變量)
一般情況下,變量(包括靜態存儲方式和動態存儲方式)的值是存放在內存中的
寄存器變量允許將局部變量的值放在CPU中的寄存器中
現在的計算機能夠識別使用頻繁的變量,從而自動地將這些變量放在寄存器中,而不需要程序設計者指定
全局變量都是存放在靜態存儲區中的。因此它們的生存期是固定的,存在於程序的整個運行過程
一般來說,外部變量是在函數的外部定義的全局變量,它的作用域是從變量的定義處開始,到本程序文件的末尾。在此作用域內,全局變量可以為程序中各個函數所引用。
-
在一個文件內擴展外部變量的作用域
外部變量有效的作用范圍只限於定義處到本文件結束。
如果用關鍵字extern對某變量作“外部變量聲明”,則可以從“聲明”處起,合法地使用該外部變量
調用函數,求3個整數中的大者。
#include <stdio.h>
int main()
{ int max( );
extern int A,B,C;
scanf(“%d %d %d”,&A,&B,&C);
printf("max is %d\n",max());
return 0;
}
int A ,B ,C;
int max( )
{ Int m;
m=A>B?A:B;
if (C>m) m=C;
return(m);
}
-
將外部變量的作用域擴展到其他文件
如果一個程序包含兩個文件,在兩個文件中都要用到同一個外部變量Num,不能分別在兩個文件中各自定義一個外部變量Num
應在任一個文件中定義外部變量Num,而在另一文件中用extern對Num作“外部變量聲明”
在編譯和連接時,系統會由此知道Num有“外部鏈接”,可以從別處找到已定義的外部變量Num,並將在另一文件中定義的外部變量num的作用域擴展到本文件
給定b的值,輸入a和m,求a*b和am的值。
文件file1.c:
#include <stdio.h>
int A;
int main()
{ int power(int);
int b=3,c,d,m; scanf("%d,%d",&A,&m);
c=A*b;
printf("%d*%d=%d\n",A,b,c);
d=power(m);
printf("%d**%d=%d\n",A,m,d);
return 0;
}
文件file2.c:
extern A;
int power(int n)
{ int i,y=1;
for(i=1;i<=n;i++)
y*=A;
return(y);
}
-
將外部變量的作用域限制在本文件中
有時在程序設計中希望某些外部變量只限於被本文件引用。這時可以在定義外部變量時加一個static聲明。



一般為了敘述方便,把建立存儲空間的變量聲明稱定義,而把不需要建立存儲空間的聲明稱為聲明
內部函數與外部函數
如果一個函數只能被本文件中其他函數所調用,它稱為內部函數。
在定義內部函數時,在函數名和函數類型的前面加static,即:
static 類型名 函數名(形參表)
如果在定義函數時,在函數首部的最左端加關鍵字extern,則此函數是外部函數,可供其他文件調用。
如函數首部可以為
extern int fun (int a, int b)
如果在定義函數時省略extern,則默認為外部函數
有一個字符串,內有若干個字符,今輸入一個字符,要求程序將字符串中該字符刪去。用外部函數實現。
思想:
分別定義3個函數用來輸入字符串、刪除字符、輸出字符串
按題目要求把以上3個函數分別放在3個文件中。main函數在另一文件中,main函數調用以上3個函數,實現題目的要求
#include <stdio.h>
int main()
{ extern void enter_string(char str[]);
extern void delete_string(char str[], char ch);
extern void print_string(char str[]);
char c,str[80];
enter_string(str);
scanf(“%c”,&c);
delete_string(str,c);
print_string(str);
return 0;
}
————————————————————————————————
void enter_string(char str[80])
{ gets(str); }
————————————————————————————————
void delete_string(char str[],char ch)
{ int i,j;
for (i=j=0;str[i]!='\0';i++)
if (str[i]!=ch) str[j++]=str[i];
str[j]='\0';
}
————————————————————————————————
void print_string(char str[])
{ printf("%s\n",str); }