记得当前刚开始学 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>
如果明确了类型,我们可以在编译期及早地发现错误,要不然只能到运行期才能发现问题,众所周知,问题发现的越晚,修改调查的成本就越高。