記得目前剛開始學 C/C++ 語言的時候,教科書說 C/C++ 是相對于機器語言的一種進階語言,那麼什麼是進階語言,也就是進階語言與機器語言本質上有什麼差別?對于這個問題,大家可能都聽過類似下面的解釋:
<pre>
機器語言是面向機器的,進階語言是面向活人的,是以進階語言理容易閱讀,容易了解
</pre>
那麼大家有沒有想過為什麼進階語言容易閱讀?這背後有什麼東西在發揮作用?
除了子產品化的思想以外,最重要的是進階語言中引入的 “類型”!
在機器語言中,沒有類型,我們在機程式時,一塊記憶體所代表的意義隻有程式員的大腦中,如果不了解代碼背後的知識,不太容易知道這塊記憶體的意義;
在進階語言中,有了類型,我們通過代碼就可以知道一塊記憶體的意義,如:
<pre>
int i;
i 占用了 4 個位元組的記憶體,類型 int 說明我們可以對這塊記憶體進行整數的算術運算操作;
</pre>
<pre>
int *p;
p 也占用了 4 個位元組的記憶體,類型為 int *,說明它是一個指向 int 類型的記憶體位址,可以進行位址運算操作
</pre>
<pre>
struct l2cap_conn_tag { ... }l2cap_conn;
l2cap_conn 占用了 n 個位元組記憶體,類型為 structl2cap_conn_tag,其類型說明了這塊記憶體代表了一個藍牙l2cap 連接配接對象,我們可以對這塊記憶體進行l2cap 相關的操作(如連接配接,發收資料,斷開等)
</pre>
可見,通過類型,我們可以明确地知道對一塊記憶體應該使用哪些操作。
使用明确的類型,我們可以寫出可讀性好的代碼,另一個方面,也能夠讓編譯器幫你檢查出更多的問題,如:
<pre>
類型不安全:
BTHANDLE hMutex;
BTHANDLE hEvent;
BTHANDLE OS_CreateMutex(void);
BTHANDLE OS_CreateEvent(void);
hEvent = OS_CreateMutex(); // 本來是要建立 mutex,可傳的 handle 卻是 event 的,由于類型都是BTHANDLE,是以編譯器不會報告這個錯誤
hMutex = OS_CreateEvent(); // 同上
明确了類型,類型安全:
typedef struct tagMutex *BTMUTEX;
typedef struct tagEvent *BTEVENT;
BTMUTEX hMutex;
BTEVENT hEvent;
BTHANDLE OS_CreateMutex(void);
BTHANDLE OS_CreateEvent(void);
hEvent = OS_CreateMutex(); // 這時編譯就會報告類型不比對,可以讓我們及早地發現代碼中的問題
</pre>
<pre>
類型不安全:
float v = 0.1f;
printf("%d\n", v); // 類型不對,應該是 %f,printf 就是類型不安全的,但多數編譯器不會報錯(gcc 可以檢查出這個問題,但隻是 gcc 的一個擴充,不是所有編譯器都支援的)
C++ 的 iostream 就是類型安全的:
std::cout << v << std::endl;
class foo { ... };
std::cout << foo <<std::endl; // 會報錯,除非重載一個新的且接受foo 作為參數的 << 操作符。
</pre>
<pre>
類型不安全:
#define FLAG1 1
#define FLAG2 2
#define FLAG3 3
BTHRESULT func(int flag);
聲明的一個函數接口,參數隻接受FLAG1、FLAG2 和 FALG3,但以下會代碼會編譯通過,編譯器不會發出警告:
func(100); // 這不是我們設計接口時所期望的,雖然可以在 func 内通過斷言或者參數判斷來處理,但這會将問題的發現時間推後,增加修改成本
明确了類型,類型安全:
typedef enum
{
FLAG1 = 1,
FLAG2 = 2,
FLAG3 = 3
} flag_t;
BTHRESULT func(flag_t flag);
func(100); // 多數編譯器會報告類型不符的錯誤或警告
func(FLAG1); // OK
</pre>
下面這個之前發貼說過一些,見:
[[http://10.10.109.234:3000/boards/8/topics/275]]
<pre>
類型不安全:
typedef int BOOL;
#define TRUE 1
#define FALSE 0
// 建立一個視窗,通過參數來指定視窗有沒有最大化,最小化按鈕,能不能由使用者改變大小
HWND CreateWindow(BOOL IsHaveMaxButton,BOOL IsHaveMinButton, BOOL IsResizable);
// 這個接口就不太好,可以看一個使用這個接口的代碼:
HWND wnd1 = CreateWindow(TRUE, TRUE,TRUE);
HWND wnd2 = CreateWindow(FALSE, TRUE,FALSE);
HWND wnd3 = CreateWindow(FALSE, FALSE,FALSE);
// 如果不看(或者不了解) CreateWindow 的聲明,或者沒有接口文檔,上面的代碼我們完全不知道在建立什麼樣的視窗
明确了類型,類型安全:
typedef enum { HAVE_MAX_BUTTON,NO_MAX_BUTTON } MaxButtonFlag;
typedef enum { HAVE_MIN_BUTTON,NO_MAX_BUTTON } MinButtonFlag;
typedef enum { RESIZABLE, FIXED }ResizeFlag;
HWND CreateWindow(MaxButtonFlag maxf,MinButtonFlag minf, ResizeFlag rf);
這樣調用方代碼就比較容易閱讀:
HWND wnd1 =CreateWindow(HAVE_MAX_BUTTON, HAVE_MIN_BUTTON, RESIZABLE);
HWND wnd2 = CreateWindow(NO_MAX_BUTTON,HAVE_MIN_BUTOTN, FIXED);
HWND wnd3 = CreateWindow(NO_MAX_BUTTON,NO_MIN_BUTTON, FIXED);
</pre>
如果明确了類型,我們可以在編譯期及早地發現錯誤,要不然隻能到運作期才能發現問題,衆所周知,問題發現的越晚,修改調查的成本就越高。