天天看點

小邵的精文

記得目前剛開始學 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>

如果明确了類型,我們可以在編譯期及早地發現錯誤,要不然隻能到運作期才能發現問題,衆所周知,問題發現的越晚,修改調查的成本就越高。