結構化程序設計主張按功能來分析需求,主要原則自頂向下,逐步求精,模塊化等。
主張按功能把軟件系統逐步細分,每個功能都負責對數據進行一次處理,每個功能接收一些數據,處理完後輸出一些數據,這種處理方式也被稱為面向數據流的處理方式。
其最小單位是函數,每個函數負責一個功能,整個軟件系統由一個個函數組成,其中作為程序入口的函數被稱為主函數。
主函數依次調用其他普通函數,普通函數之間依次調用,從而完成整個軟件系統的功能。
一個C程序可由一個主函數和若干個其他函數構成,主函數調用其他函數,其他函數也可互相調用。
函數定義的一般形式:(無參函數/有參函數/空函數)
無參函數形式:
類型標識符 函數名()
{
聲明部分;
語句部分;
}
有參函數形式:
類型標識符 函數名(形式參數列表)
{
聲明部分;
語句部分;
}
空函數形式:在程序開發初始階段,可以在將來准備擴充功能的地方寫上一個空函數,采用實際函數名,只是這些函數未編好,先占一個位置,以後用一個編好的函數代替它。
類型說明符 函數名()
{
}
函數參數和函數的值
形參與實參
定義函數時,函數名括號裡的參數為形參;
調用函數時,被調用函數括號裡的為實參;
關於形參與實參的說明:
1)在定義函數中指定的參數,在未出現函數調用時,它們並不占內存中的存儲單元。在發生函數調用時才分配內存單元。調用結束後,形參占用的內存也被釋放。
2)實參可以是常量,變量或表達式。
3)在被定義的函數中,必須指定形參的類型。
4)實參與形參的類型應相同或兼容。
5)實參向形參傳遞數據,“值傳遞”,單向的
6)返回值由return語句獲得。如果函數值的類型與return語句中的表達式不一致,則以函數類型為准。即函數類型覺得返回值的類型。
7)對於不帶返回值的函數,用void定義函數為無類型(空類型);
函數調用:按函數在程序中出現的位置來分,可以分為三種調用方式。
函數的嵌套調用
C語言不可嵌套定義函數,函數之間是平等的,但可以嵌套定義函數。
//用截距法求方程 #include<stdio.h> #include<math.h> //define function f float f(float x) { float y; y=((x-5)*x+16)*x-80; return y; } //define function of cross point with x: xpoint float xpoint(float x1,float x2) { return ((x1*f(x2)-x2*f(x1))/(f(x2)-f(x1))); } // define root function float root(float x1,float x2) { float x,y,y1; y1=f(x1); do{ x=xpoint(x1,x2); y=f(x); if(y*y1>0) { y1=y; x1=x; } else x2=x; }while(fabs(y)>=0.00001); return x; } int main() { float x1,x2,f1,f2,x; do { printf("input x1,x2:\n"); scanf("%f %f",&x1,&x2); f1=f(x1); f2=f(x2); } while(f1*f2>=0); x=root(x1,x2); printf("A root of equation is %f\n",x); }
函數的遞歸調用
在定義一個函數的過程中有出現直接或間接地調用該函數本身,稱為函數的遞歸調用。
int f(int x) { int y,z; z=f(y); return z; }
//求階層 #include<stdio.h> float frac(int n) { float f; if(n<0) { printf("error"); } else if(n==0||n==1) f=1; else f=frac(n-1)*n; return f; } int main() { float y; int n; printf("input an integer:"); scanf("%d",&n); y=frac(n); printf("%d!=%10f\n",n,y); }
數組作為函數參數
數組名可以做形參和實參,傳遞數組首地址;
#include<stdio.h> float average(float a[10]) { int i; float aver,sum=0; for(i=0;i<10;i++) { sum=a[i]+sum; } aver=sum/10; return aver; } int main() { 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("%f\n",aver); }
說明:1)用數組名做函數參數,應該在主調函數和被調函數分別定義數組;例如score,a,數組。
2)實參數與形參類型一致。
3)在被調用函數中聲明了形參數組大小為10,但在實際上,指定其大小是不起任何作用的,因為C語言編譯對形參數組大小不做檢查,只是將實參數組的首元素的地址傳給形參數組。因此,形參數組名獲得了實參數組的首元素地址。它們共占統一地址,同一存儲單元。score[n]和a[n]具有相同的值。
4)形參數組可以不指定大小,在定義數組時在數組名後面跟一個空的括號。也可設置另一個形參。
5)用數組名作函數實參時,不是把數組元素的值傳遞給形參,而是把實參數組的元素的地址傳遞給形參數組。這樣兩個數組共占用一段內存單元。
局部變量和全局變量:從變量的作用域(空間)角度來分。
局部變量:在一個函數內部定義的變量是內部變量,只在函數范圍內有效。本函數才能使用它們。
1)主函數中定義的變量(m,n)也只在主函數中有效,主函數也不能使用其他函數中定義的變量。
2)不同函數中可以使用相同名字的變量,它們代表不同的對象,互不干擾。
3)形參也是局部變量。
4)在一個函數內部,可以在復合語句中定義變量,這些變量只在本復合語句中有效。
全局變量
對應於局部變量,在函數外部定義的變量稱為外部變量,全局變量。其他函數可以調用。
全局變量增加了函數間數據聯系的渠道。一般將全局變量名的第一個字母大寫。
1)全局變量在程序的全部執行過程都占用內存單元,而不是僅在需要時開辟單元。
2)它使函數的通用性降低了,因為函數在執行時要依賴於其所在的外部變量。
3)使用全局變量過多,會降低程序的清晰性,人們往往難以清楚地判斷出每個瞬間各個外部變量的值。因此,要限制外部變量。
4)如果同一源文件中,外部變量與局部變量同名,則在局部變量的作用范圍內,外部變量被“屏蔽”,即它不起作用。
變量的存儲類別
動態存儲方式與靜態存儲方式(從變量值存在的時間(生存期))
靜態存儲方式:在程序運行期間由系統分配固定的存儲空間的方式。
動態存儲方式:在程序運行期間根據需要進行動態分配存儲空間的方式。
全局變量全部放在靜態存儲區,在程序開始時分配空間,占據固定的內存單元。程序執行結束釋放內存。
動態存儲區存放以下數據:
1)函數形參
2)自動變量(auto)
3)函數調用時的現場保護和返回值等
以上數據在函數調用開始時分配動態內存空間,函數結束釋放。如果一個程序調用兩次同一函數,分別給函數形參不同的存儲空間。
變量有兩個屬性:存儲類型(數據在內存中存儲的方式:動態和靜態)和數據類型
具體包括四種:
auto變量
如果不專門聲明為static來存儲類別,都是動態分配存儲空間的。
用static聲明局部變量
有時希望函數中局部變量的值在函數調用後不消失而保留原值,即其占用內存單元不釋放,在下一次調用該函數時,該變量已經有值,即上一次函數調用結束時的值。
1)靜態句變量屬於靜態存儲類別,在靜態存儲區分配空間。整個運行過程不釋放內存。
2)只賦初值一次。
3)如果定義局部變量時不賦值的話,則對靜態局部變量來說,編譯時自動賦初值0或空字符;
對自動變量而言,不賦初值則分配一個不確定的值。
4)雖然靜態局部變量在函數調用結束後仍然存在,但其他函數是不能引用它的。
register寄存器變量
c語言語序將局部變量的值放在CPU中的寄存器中,需要用時直接從寄存器取出參加運算,不必再到內存中去存取,這樣提高效率。用關鍵字 register聲明。
1)只有局部自動變量和形參可以作為寄存器變量,其他(全局)不行。
2)由於計算機中寄存器數目有限,不能定義任意多個寄存器變量;
3)局部靜態變量不能定義為寄存器變量;
用extern聲明外部變量
外部變量時在函數的外部定義的全局變量,它的作用域時從變量的定義處開始,到本程序文件的尾部。編譯時分配在靜態存儲區。有時需要用extern聲明外部變量,以擴展外部變量的作用域。
用static聲明外部變量
在程序設計中希望某些外部變量只限於被本文件引用,而不能被其他文件引用;
在程序設計過程中,常由若干人分別完成不同的各個模塊,各人可以獨立地在其設計的文件中使用相同的外部變量名而互不相干。只需要在每個文件的開頭加上static聲明即可。
內部函數和外部函數
函數本質上是全局的,因為一個函數要被另外的函數調用,但也可以指定函數不能被其他文件調用。根據函數能否被其他源文件調用,將函數分為內部函數和外部函數;
內部函數(靜態函數)
如果一個函數只能被本文件中的其他函數所調用,它稱為內部函數。函數的作用域只局限於所在文件,在不同的文件中有同名的內部函數,互不干擾。在函數名和函數類型前面加static;
static int fun(int a, int b);
外部函數
1)在定義函數時,如果在函數首部的最左端加關鍵字extern,則表示此函數時外部函數,可供其他函數調用。默認為外部函數。
2)在需要調用此函數的文件中,用extern對函數作聲明,表示該函數是在其他文件中定義的外部函數。