- 函數
6、1概述
1)一個較大的程序可以分爲若干個程序模塊,每一個模塊用來實現一個特定的功能。
在高級語言中用子程序實現模塊的功能。子程序由函數來完成。
一個C程序可由一個主函數和若干其他函數構成。
函數間的調用關係:
2)由主函數調用其他函數,其他函數也可以互相調用。同一個函數可以被調用多次。
說明:一個C程序由一個或多個程序模塊組成,每一個程序模塊作爲一個源程序文件。對較大的程序,一般不希望把所有內容全部放在一個文件中,而是將他們分別放在若干個源文件中,再由若干源程序文件組成一個C程序。這樣便於分別編寫,分別編譯,提高調試效率。一個源程序文件可以爲多個C程序公用。
- C程序的執行是從main函數開始的,如是在main函數中調用其他函數,在調用後流程返回到main函數中,在main函數中結束整個程序的運行。
- 所有的函數都是平行的,在定義函數的時候是分別進行的,是互相獨立的。一個函數並不從屬與另一個函數,函數不能嵌套定義。
- 從用戶使用的角度看,函數有兩種:
①標準函數,即庫函數。這是由系統提供的,用戶不用自己定義。
②自己定義函數
- 從函數的形式看,函數分爲兩類
①無參函數。在調用函數的過程中,主調函數不向被調用函數傳遞數據。無參函數一般用來執行指定的一組操作。
②有參數函數。在調用函數時,主調函數在調用被調用函數時,通過參數向被調用函數傳遞數據,一般情況下,執行函數會得到一個值。
- 定義無參數函數的一般形式:
類型標識符 函數名()
{
聲明部分
語句部分
}
- 定義有參數
類型標識符 函數名(形式參數列表)
{
聲明部分
語句部分
}
- 定義空函數的一般形式
類型標識符 函數名(){}
調用此函數的時候,什麼工作也不做,沒有任何實際作用。
6、2形式參數和實際參數
在前面提到的有參數函數中,在定義函數時函數名後面括弧中的變量名稱爲“形式參數”。
在主調函數中調用一個函數時,函數名後面括弧中的參數稱爲實際參數。
關於形參與實參的一些重要的說明:
- 在定義函數中指定的形參,在未出現函數調用時,他們並不佔用內存中的存儲單元。只有在發生函數調用時,函數形參才能被分配內存單元。在調用結束後,形參所佔的內存單元也被釋放。
- 實參可以是常量、變量或表達式,但要求他們有確定的值。在調用時將實參的值賦值給形參。
- 在定義的函數中,必須指定形參的類型
- 實參與形參的類型應相同或賦值兼容。
- 在C語言中,實參向對形參的數據傳遞是“值傳遞”,單向傳遞,只由實參傳給形參,而不能由形參傳回來給實參。在內存中,實參單元與形參單元是不同的單元。
6、3函數參數和函數的值
主調函數和被調函數之間有數據傳遞的關係。Return後面的括弧中的值()作爲函數帶回來的值(稱爲函數返回值)
在不同的函數之間傳遞數據,可以使用的法:
參數:通過形式 參數和實際參數
返回值:用return語句返回計算結果。
全局變量:外部變量
6、4函數的返回值
希望通過函數調用使主函數能得到一個確定的值,這就是函數的返回值。
- 函數返回值是通過函數中的return語句獲得的。
return函數將被調用語句中的一個確定的值帶回主調函數中去。
- 一個函數中可以有一個以上的return語句,執行到哪一個return語句,哪一個起作用
- 函數的返回值應當屬於某一個確定的類型,在定義函數的時候指定返回值。
- 在C語言中,凡是不加類型說明的函數,自動按照整型處理。
- 在定義函數時,指定的函數類型一般應該和return語句中的表達式類型保持一致。
- 沒有返回值得函數,應該用void定義函數爲“無類型”。
6、5函數的調用
函數調用的一般格式爲:函數名(實參列表)
如果是調用無參函數,則“實參列表”可以沒有,但是括弧不能省略。
1)如果實參列表包含多個實參,則各參數間用逗號隔開。實參與形參的個數應該相等,類型應該匹配。實參與形參按照順序,--傳遞數據。
2)如果實參列表包括多個實參,對實參求值的順序並不是確定的,有的系統按照自左向右順序求實參的值,有的系統則按照自右向左順序。
3)函數的調用方式
①函數語句
②函數表達式
③函數參數(嵌套使用)
- 函數的聲明以及函數原型
自己定義函數或者是庫函數
- 如果是庫函數,還需要使用include導入文件頭
- 函數使用之前要先聲明,如果函數的定義在被調用之前,可以不用聲明
函數的定義和聲明不是一回事。函數的定義是指對函數功能的確立,包括指定函數名,函數值類型,形參及其類型,函數體等。他是一個完整的、獨立的函數單位。函數的聲明的作用則是把函數的名字,函數類型以及形參的類型、個數和順序通知編譯系統,以便於在調用該函數的時候系統按此進行對照檢查。
補充:遞歸算法
在調用函數的時候,又出現直接或間接的遞歸調用本身的現象。
在遞歸中必須要有一個退出的條件。
6、6數組作爲函數參數
數組可以作爲函數參數使用,進行數據傳送。數組用作函數參數有兩種形式:
- 數組元素作爲實參
數組元素就是下標變量,它與普通變量沒有區別。單向值傳遞
在普通變量或下標作函數參數的時候,形參變量和實參變量是由編譯系統分配的兩個不同的內存單元。在函數調用的時候發生的值傳遞是把實參變量的值賦予形參變量。
- 數組名作函數的參數
用數組名作函數參數時,則要求形參和相對應的實參都必須是類型相同的數組,都必須有明確的數組說明。當形參和實參二者不一致的時候,會發生錯誤。
在用數組名作函數參數時,不是進行值得傳遞,即不是把實參數組的每一個元素的值都賦予形參數組的各個元素。因爲數組名就是數組的首地址。因此在數組名作函數參數時所進行的傳送只是地址的傳送,也就是說把實參數組的首地址賦予形參數組名。
6、7局部變量與全局變量
6、7、1局部變量
- 形式參數也是局部變量。
- 在一個函數內部,可以在複合語句中定義變量,這些變量只在本複合語句中有效,這種語句也被稱爲“分程序”或“程序塊”。
6、7、2全局變量
函數之外定義的是外部變量,外部變量是全局變量。
全局變量是從該變量定義開始到結束纔會銷燬。
在使用全局變量的時候,它使函數的通用性降低了,可移植性也降低了。如果將一個函數轉移到另一個文件中,還要將有關的外部變量及其值一起移植過去。
6、8動態存儲與靜態存儲
從變量值的存在時間(即生存期)角度來分,又可以分爲靜態存儲方式和動態存儲方式。
所謂靜態存儲方式是指在程序運行開始的時候由系統分配固定的存儲空間的方式。
而動態存儲方式則是在程序運行期間根據需要進行動態的分配存儲空間的方式。
用戶存儲空間可以分爲三部分:
- 程序區
- 靜態存儲區
- 動態存儲區
6、9變量的存儲類別
在C語言中每一個變量和函數有兩個屬性:數據類型和數據的存儲類別。
對於數據型(如整型,字符型等)。存儲類別指的是數據在內存中存儲的方式。
存儲方式分爲兩大類:靜態存儲類和動態存儲類。
具體包含四種:自動的(不加static),靜態的(加static),寄存器的,外部的。根據變量的存儲類型,可以知道變量的作用域和生存期。
6、9、1用static聲明靜態局部變量
有時候希望函數中的局部變量的值在函數調用結束後不消失而保留原值,即其佔用的存儲單元不釋放,在下一次該函數調用的時候,該變量已有值,就是上一次函數調用結束時的值。
例如
void main()
{
static int x = 12;
void getd(int x);
void getd(int x);
getd(x);
getd(x);
printf("%d\n",x);
}
void getd(int x) {
static int y = 3;
x++;
y++;
printf("%d\n",y);
}
靜態局部變量的說明:
- 靜態局部變量屬於靜態存儲類別,在存儲區內分配存儲單元。在程序整個運行期間都不能釋放。
- 自動變量(即動態局部變量)屬於動態存儲類別,佔動態存儲區空間而不佔靜態存儲區空間,函數調用結束後即釋放。
- 對靜態局部變量是在編譯時賦初值,即只付初值一次,在程序運行時它已有初值。以後每次調用函數時不再重新賦初值而只是保留上次函數調用結束時的值。
- 對於自動變量賦初值,不是在編譯時進行的,而是在函數調用的時候運行的,每次調用一次函數重新給一次初值,相當於執行一次賦值語句。
- 雖然靜態局部變量在函數調用結束後仍然存在,但是其他函數是不能引用的。
6、9、2register變量
一般情況下,變量(包括靜態存儲方式和動態存儲方式)的值是存放在內存中的。
如果有一些變量使用頻繁(例如在一個函數中執行10000次循環,每次循環中都要引用它),則爲存取變量的值要花費不少時間。
爲提高執行的效率,C語言允許將局部變量的值放在CPU中的寄存器中,需要用時直接從寄存器取出參加運算,不必再到內存中去取。
由於對寄存器的存取速度遠高於對內存的存取速度,因此這樣做可以提高執行效率。這種變量叫做寄存器變量。用register聲明。
6、9、3用extern聲明外部變量
外部變量即全局變量,它的作用域是從變量的定義處開始到本程序文件的末尾。
在此作用域內,全局變量可以爲程序中各個函數所引用。編譯時將外部的變量分配在靜態存儲區。
有時需要使用extern來聲明外部變量,以擴展外部變量的作用域。
6、9、4在多文件的程序中聲明外部變量
- 用static聲明外部變量
有時在程序設計上中希望某些外部變量只限於被文件引用,而不能被其他文件引用。這時可以在定義外部變量的時候加一個static。
6、10內部函數與外部函數
函數本質上是全局的,因爲一個函數要被另外的函數調用,但是也可以指定函數不能被其他文件調用。
我們根據函數能否被其他文件調用,將函數區分爲內部函數與外部函數。
6、10、1內部函數
如果一個函數只能被本文件中其他函數所調用,它稱爲內部函數。
在定義內部函數的時候,在函數和函數類型的前面加static。
即static 類型標識符 函數名(形參表) 如static int fun(int a,int b)
6、10、2外部函數
在定義函數時,如果在函數首部的最左端加上關鍵字extern,則表示此函數是外部函數,可以供其他文件調用。如函數首部可以寫爲 extern int fun(int a,int b),如果省略extern 的話默認爲外部函數。