標籤:
C是一門結構化的程式設計語言,Objective-C是C的超集。
為什麼要定義函數?
為了方便複用某段代碼,可以將這段代碼定義成一個函數,以後每次調用該函數,就相當於讓程式去執行這段代碼。
定義函數(function)的文法
返回值類型 函數名(形參列表)
{
}
返回值類型:可以是任何有效類型(基本類型、構造類型、參考型別)。
所謂返回值類型,決定了該函數執行成功之後,所返回的結果的類型。
因此如果沒有返回值類型,應該將返回值型別宣告為void。
如果不寫返回值類型,表明返回值類型為int。
如果聲明了返回值類型(包括不寫),程式應該在函數體使用return語句來返回函數的返回值。
函數名:標識符即可。
實際上,函數名應該一個或多個有意義的單詞連綴而成。
形參列表: 形參類型 形參名, 形參類型 形參名, ...
每個形參都必須由 形參類型和形參名所組成。
調用函數
注意點:
1. 聲明函數時指定了幾個形參,調用函數時就需要傳入幾個參數、而且參數的類型要一致。
2. 函數的返回值,既可用變數去裝;也可立即拿來用掉(將函數返回值放入運算式中)。
函式宣告
C語言是從上到下的,預設要求:函數必須先定義、再使用。
對於如下兩種情況:
- 函數定義在函數調用處的後面。
- 函數定義在另一個源檔案中。
此時就需要使用函式宣告 —— 函式宣告就是告訴編譯器:這個函數是存在的。
函式宣告:就是重要函數的返回值類型、函數名、形參類型即可,不要函數執行體。
注意:函式宣告時既可有形參名,也可沒有形參名。
import語句,就是匯入大量的函式宣告。
由於函式宣告必須在所有函數調用的前面,因此程式就需要將#import語句放在程式在最上面。
函數的傳參機制
Object-C的傳參機制是“值傳遞”,程式傳入函數的只是參數本身的副本(複製品),
因此函數所參數所作的修改,對參數本身是沒有任何影響的。
注意:對於傳遞指標的情形,實際上傳遞的依然是指標的複製品(副本)。
遞迴函式
如果一個函數,調用了自身,該函數就會形成遞迴函式。
遞迴,可實現一個隱式的迴圈。
與迴圈類似的是,遞迴也必須有結束的時候。
遞迴:一定要有結束的時,要向已知的方向遞迴。
使用數組本身(數組的本質就是指標)作為參數
注意:
1. 當在函數中聲明數群組類型的參數時,既可指定數組的長度,也可不指定數組的長度,但通常都不會指定。
2. 由於數組本身是一個指標,因此傳入函數的並不是數組本身,只是一個指標的副本。
因此函數無法通過sizeof來擷取被傳入的數組的長度。
3. 由於數組本身是指標,因此函數對數組元素所做的修改會影響數組本身的元素的。
內建函式與外部函數
C語言是結構化程式設計語言,因此函數是C的一等公民:
一個C程式通常是由於很多個函數組成,程式員開發的函數,可能還需要依賴系統內建的函數。
由此可見:C語言程式,可能由成千上萬的函數組成,但最多隻能有一個main函數
—— 它作為程式的入口,其他函數都是直接或間接地被main函數調用,所有函數的地位是平等。
每個函數都可調用別的函數,也可被別的函數調用。
static修飾符的作用之一:把外部東西(函數、全域變數),變成內部東西(函數、全域變數)。
- static函數,就是內建函式——只能在當前源檔案中被調用。
- 沒static修飾或extern修飾的函數,可以任何地方被調用(只要先做函式宣告即可)。
對於內建函式,程式只有通過直接把原始碼import進來才可以調用內建函式。
而外部函數,程式即使拿不到定義函數的原始碼,只要有定義函數的原始碼產生的庫,程式也可通過函式宣告之後來調用外部函數。
局部變數與全域變數
C的變數分為:
局部(local)變數:在函數裡、方法裡定義的變數,都是局部變數。
局部變數儲存在各自的棧區中,函數、方法執行結束時,棧區被銷毀。
特徵:只在定義該變數的函數、方法內有效,函數、方法執行完,局部變數立即消失。
全域(global)變數:在檔案範圍內定義的變數,都是全域變數。
全域變數儲存在整個程式的記憶體(靜態儲存區)中。程式不結束,靜態儲存區不會消失。
特徵:只要程式不退出,全域變數將一直有效。
細分局部變數,又可分為:
- 形參:從函數被調用開始,形參開始生效,函數結束時,參數失效。
- 方法裡局部變數:從定義該變數開始生效,函數結束時,該變數失效。
- 代碼塊的局部變數:從定義該變數開始生效,代碼塊結束時,該變數失效。
包括在if條件體、迴圈體、for迴圈的初始化語句中定義的變數,都屬於代碼塊的局部變數,離開了定義該變數的花括弧地區,那麼該變數就失效了。
細分全域變數
- static全域變數,就是內部[全域]變數——只能在當前源檔案中被調用。
- 沒static修飾的全域變數,可以任何地方被調用(只要先做變數聲明即可)。
變數聲明:如果程式要使用其他來源程式中定義的全域變數時,就必須做變數聲明。文法是:
extern 類型變數名;
局部變數的儲存機制:
定義局部變數時,有如下幾個修飾符:
auto - 有auto和沒auto效果是一樣的。也就是說,你定義的所有的局部變數,預設就相當於有個auto。
auto修飾的局部變數,會被儲存到函數或方法的棧區中。
static - 把局部變數放到靜態儲存區中。
對於static的局部變數而言,只要程式不退出,靜態局部變數將一直有效。
由於static局部變數會一直有效,因此程式只有第一次調用函數時,才會對static局部變數賦初始值。
register - 控制把局部變數放入寄存器中。
但OS並不保證register變數一定會進入寄存器,所以實際上這個修飾用的並不多。
關於定義變數的原則:
1. 程式應該優先使用局部變數(全域變數是非常糟糕的設計、會造成牽一髮而動全身的效果)。
2. 如果局部變數需要能在函數、方法結束時,依然可以儲存上次啟動並執行資料,或者該變數只希望第一次調用時被初始化,
該局部變數應該用static修飾。
3. 實在沒辦法,才考慮使用全域變數。
典型的,當一個程式單元需要把資料共用給另外一個程式單元時,正確做法是參數傳遞,而不是全域變數。
static修飾符有2個作用:
- 修飾全域函數、全域變數,使之變成內建函式、內部全域變數。
- 修飾局部變數,使之成為靜態局部變數——靜態局部變數會被儲存到程式的靜態儲存區。
extern修飾符也有2個作用:
- 聲明全域變數。
- 修飾外部函數。————但實際上,不用extern效果也是一樣的。
預先處理
預先處理:程式在編譯階段就會處理調用東西,這些預先處理指令不會保留到運行階段。
對於來源程式中包含的預先處理,編譯器會在編譯階段處理完成,到運行時就不存在預先處理指令。
預先處理的特徵:
- 通常在程式的開頭。
- 必須以#開始。
#define 或 #undef定義宏變數和取消宏
#define定義的又被稱為:“宏(macro)變數”。
#define的作用是:編譯器在處理常式之前,先執行尋找、替換。
通過#define定義宏變數有兩個好處:
- 可以方便程式以後修改。
- 可以提高程式的可讀性。
#undef:取消宏定義
#define還可以定義帶參數的宏
對於帶參數的宏定義而言,有兩點要注意:
- 定義宏的參數時,並不是定義函數的參數,因此宏的參數無需指定參數類型。
- 使用宏的參數時,一定要參數放在圓括弧中。
使用#ifdef #ifndef #else #endif執行條件編譯。
它的用法基本上和前面介紹的分支是一樣的。
它們的主要作用是判斷指定的宏是否存在,根據指定宏是否存在來執行相應的分支。
使用#if #elif #else#endif執行條件編譯。
預先處理的if分支比普通的if分支的效率要高的多。
能用預先處理的if分支,應該盡量使用預先處理的if分支。
記住:只要你的if條件中包含了變數、函數調用等運算式,就只能使用普通的if分支。
#include和#import
#include和#import的功能是類似的,但#import的功能更強大一些。
#import就可以自動避免重複include導致的錯誤。
Object-C基礎(4)—— 函數