對高級程序設計語言的類型系統的基本理解

類型系統

在個人《對高級程序設計語言的基本理解》一文中我曾經闡述了本身對高級程序設計語言的理解,其中就說道了數據類型的重要性,而一門高級程序設計語言的不少甚至大部分特性都是由於類型系統而決定的。這篇文章的目的就是聊一聊我對高級程序設計語言的類型系統的理解,經過類型系統就可以瞭解到一門高級程序設計語言的一些基本特性。python

所以樹立一種"類型思惟"在學習一門高級程序設計語言中是很是有必要的。編程

咱們在描述一門語言的類型系統的時候,一般有強類型弱類型靜態類型動態類型這四個術語。對於這四個術語,在網上找到的解釋都很是的雜亂,並且有不少的解釋是明顯錯誤的。好比有的網友在解釋Python語言爲何被定義爲強類型的語言的時候,使用了以下的例子:bash

a = 1
b = "2"
c = a + b //error
複製代碼

可是一樣做爲強類型語言的Java中,字符串和數字相加並不會報錯,這說明上述的解釋並不許確。編程語言

在這篇文章中,我會談一談我對這些概念的理解,同時會引入一些我本身的概念來完善個人"類型思惟"。函數

靜態類型和動態類型

《對高級程序設計語言的基本理解》已經說過了數據類型在高級程序設計語言中的意義,一門高級程序設計語言一般會定義多種基本的數據類型,而這些數據類型實際上是表明了一個固定的內存長度和對對應的內存中的數據的處理方式。工具

說到這裏,咱們還須要稍微瞭解一點程序的運行機制,程序在運行的時候須要有一個基地址,而後CPU經過基地址+偏移量的機制對數據和指令進行尋址,從而使得程序運行起來(固然這是一個比較簡單的歸納,實際狀況很是複雜)。post

而咱們又知道,高級程序設計語言必須通過必定的轉換才能在計算機中運行。通常來講高級程序設計語言能夠大體分爲兩類——解釋執行型和編譯執行型(固然實際狀況也會更加複雜)。學習

經過上面的分析以後,咱們來明確兩個概念,就是靜態動態。通常咱們稱解釋/編譯階段爲靜態階段,而成運行階段爲動態階段ui

靜態類型

有了上面的認識,那麼所謂的靜態類型指的就是變量的類型(該變量的偏移量)在靜態階段就要肯定下來,這就意味着,在靜態階段程序就可以肯定每一個變量的偏移量是多少。這就意味着靜態類型語言須要具有下列的這些特徵:編碼

  • 變量必須先聲明後使用。
  • 聲明某個變量的時候要麼顯式指定一個類型,要麼進行初始化依靠類型推斷肯定一個類型,而且變量的類型一旦肯定以後將不能再進行更改。
  • 數據類型的定義粒度要比較細,好比數字類型不能只使用一個number類型,應該定義對應的整型,浮點型等,對不一樣偏移量的值進行支持

靜態類型檢查

這裏我須要引入一個自定義的概念,即靜態類型檢查,什麼是靜態類型檢查呢?意思是程序的編譯器或者解釋器在靜態階段會對每一個變量的類型進行檢測,而且不容許同一個變量前後保存不一樣類型的值,若是要存,要麼類型兼容,要麼把被存儲的值轉換成變量的類型。同時,對於操做符的操做數、函數的參數的類型都會作對應的類型檢查[1]

爲何要引入靜態類型檢查這個術語呢,由於靜態類型檢查對於靜態類型的語言來講是必須的,由於靜態類型語言須要在靜態階段肯定變量的偏移量,那麼變量的類型就必須具備不變性,不然將沒法在靜態階段就肯定一個變量的偏移量

而某些語言雖然是動態類型的,可是卻提供了靜態類型檢查的功能,好比具備工具屬性的TypeScript語言。

動態類型

動態類型在編碼上會比靜態類型的語言更加靈活,由於在靜態階段不須要肯定變量的類型,一個變量的類型(偏移量)是在運行階段才肯定的。這就意味着上面靜態類型所具備的那些特徵對動態類型來講並非必須的。

動態類型檢查(運行時類型檢測(RTTI))

這裏我須要引入一個概念,即動態類型檢測,也能夠成爲運行時類型檢測(RTTI),不論是動態類型語言,仍是靜態類型語言,RTTI都是必須的,由於有時候數據類型的轉換是不可避免的。

可是須要注意的是,RTTI的類型檢測更多的是檢測值的類型,而不是變量的類型,同時對於動態強類型語言,對類型的檢測所有發生在動態階段

靜態類型和動態類型的對比

1565520846800

以上的JavaScript是動態類型的語言,而C++是靜態類型的語言,上述兩段代碼實現的功能是同樣的,咱們從內存分配的角度分析一下動態類型和靜態類型的區別

1565521806470

能夠看到,JavaScript須要在運行的時候動態計算各個屬性的偏移量,而且每一個對象都要維護本身的屬性的偏移量信息,而C++在編譯的時候就已經肯定好了每一個對象的屬性的偏移量,偏移量信息只須要保存一份就能夠了。

同時,靜態類型可以實現子文檔化,由於靜態類型的語言確定會有靜態類型檢查的特性,配合IDE,寫代碼會更加方便。

強類型和弱類型

對於強類型和弱類型這兩個概念並無一個明確的定義,而對於這兩個定義,網上的解釋也比較雜亂,這裏我根據本身的理解給出一個自認爲比較通俗的解釋。

首先,對於一門編程語言來講,最基礎的部分就是類型系統,操做符,關鍵字和語句,有了這些最基礎的部分,咱們就能夠實現任何複雜的程序。同時,咱們最終操做數據的方式都是經過編程語言爲咱們定義的這些基礎部分來進行的,這裏我對強類型和弱類型的討論主要是針對操做符來進行的。

咱們都知道,編程語言在定義一個操做符的時候都會定義操做符的操做數數量以及操做符指望接收的操做數的數據類型,那麼下面咱們就展開討論。

強類型

強類型所表現出來的特徵是,若是操做符接收到的操做數的數據類型和定義操做符時所規定的數據類型不相符的時候,就會直接報告異常,靜態類型的語言或者是具備靜態類型檢查的動態類型語言會在靜態階段報告錯誤,而不具備靜態類型檢查的動態類型語言會在運行時報告錯誤。這時候編程語言對數據類型的要求比較強制或者是對數據類型比較敏感,咱們稱之爲強類型。典型的強類型語言有Java、Python等。下面給出幾個Python中操做符定義的例子:

二元算術運算符遵循傳統的優先級。 請注意某些此類運算符也做用於特定的非數字類型(對於支持非數字類型的運算符給出了專門的說明)。 除冪運算符之外只有兩個優先級別,一個做用於乘法型運算符,另外一個做用於加法型運算符:

運算符 * (乘) 將輸出其參數的乘積。 兩個參數或者必須都爲數字,或者一個參數必須爲整數而另外一個參數必須爲序列。 在前一種狀況下,兩個數字將被轉換爲相同類型而後相乘。 在後一種狀況下,將執行序列的重複;重複因子爲負數將輸出空序列。

...

運算符 + (addition) 將輸出其參數的和。 兩個參數或者必須都爲數字,或者都爲相同類型的序列。 在前一種狀況下,兩個數字將被轉換爲相同類型而後相加。 在後一種狀況下,將執行序列拼接操做。

運算符 - (減) 將輸出其參數的差。 兩個數字參數將先被轉換爲相同類型(由於對於數字類型來講,只有number一種,可是在運行時有可能會有整型和浮點型之分)。

這是Python官方文檔對二元+操做符的定義,這解釋了爲何1+"2"這樣的表達式在Python中沒法運行。同時注意以下代碼:

a = "123"
b = 1
print(a/b)
複製代碼
Traceback (most recent call last):
  File "H:/Python/Hello/src/Hello.py", line 3, in <module>
    print(a/b)
TypeError: unsupported operand type(s) for /: 'str' and 'int'
複製代碼

能夠看到在將一個str類型和一個number類型作整除操做的時候直接報告異常了。

弱類型

弱類型表現爲對類型的容忍度較高,若是操做符接收到的操做數的數據類型和定義操做符是定義的數據類型不相符的時候,不會直接報出異常,而是首先嚐試將接收到的值轉換爲操做符指望的數據類型的值,而後再使用操做符進行運算,若是數據類型轉換失敗,最終纔會拋出異常。這時候咱們稱這門語言對類型的要求比較弱,或者是對類型不敏感。

這樣的數據類型轉換通常會在運行時進行,固然咱們也能夠在代碼中對某個值進行強制類型轉換。

好比JavaScript中,充斥着大量的隱式類型轉換,並且在ES規範中定義了很是多的抽象操做來對這些隱式地類型轉換提供操做,關於JavaScript這一部分的更多內容在JavaScript部分會有介紹。

強類型和弱類型的對比

  • 強類型語言的程序健壯性和可讀性都比較好,而弱類型的語言操做符的使用更加靈活,可是須要具備較強的把控能力。

C語言的疑惑

C語言被歸類爲靜態弱類型語言,按照我上面的論述來講也是能夠說的通的,以下代碼:

int main() {
    int a = 0;
    double b = 0;
    a = "123" - 3; //沒有報錯,可是產生的行爲比較怪異,沒有仔細研究過,可是足以說明C語言是一門弱類型語言
    printf("sizeof a: %d", sizeof(a)); //4 數據類型(偏移量)沒有發生變化,因此是靜態類型
    printf("\n");
    printf("valueof a: %d", a);
    printf("\n");
    printf("sizeof b: %d", sizeof(b)); //8
}
複製代碼

真值和假值

布爾類型在任何一門高級程序設計語言中都是一個使用頻率至關高的數據類型,由於任何的邏輯判斷的結果都是由布爾類型的值表示的。爲了編碼方便,不少操做語言的類型系統都引入了真值假值的概念。

當一個不是bool類型的值轉化成bool類型以後若是是true,咱們就稱其爲真值,反之,若是一個不是bool類型的值轉化爲bool類型值後值爲false,那麼咱們就稱這個值爲假值。這樣就至關於在任何數據類型中都有跟true和false等價的值,這樣咱們在進行邏輯判斷的時候就可以依賴真值和假值的特性。

同時,在動態類型的語言中,引入真假值會讓邏輯操做符擁有更增強大的功能。

總結

以上就是我對高級程序設計語言的類型系統的基本認識,有了這些基本認識以後,再學習一門新的語言的時候先搞清楚語言的定位,可以節省不少時間。

以上純屬我的觀點,有不一樣的觀點歡迎進行討論。


  1. 有不少人可能認爲這是強類型所表現出來的特徵,可是經過我對多門語言的分析發現,將這個特性冠在強類型上有失偏頗,好比Python語言是一門動態強類型的語言,可是一個變量能夠前後存儲不一樣類型的值。 ↩︎

相關文章
相關標籤/搜索