在 K&R 經典教材 The C Programming Language 的2.2節中,對 int 類型是這樣描述的
an integer, typically reflecting the natural size of integers on the host machine
意思是反映了機器整數類型的 natural size,可是,
這個 natural size 又是什麼意思呢?
書中後來在談到 short, int, long 的關系時,又說,這些類型由編譯器根據機器自由選擇合適的大小,但是 short 和 int 至少 16 位,long 至少 32 位。
這裡的問題是
編譯器是根據什麼決定類型大小呢?
後面書中又提到,這些類型啊,在
中都有,我就在ubuntu下查看了 /usr/include/limits.h
,裡面確實提到
/* Minimum and maximum values a `signed int' can hold. */ # define INT_MIN (-INT_MAX - 1) # define INT_MAX 2147483647
但是,這也是一種定義,還是沒有說出為什麼,我現在想知道的是
為什麼
於是,我想起了那些年掃過的 《深入理解計算機系統》,英文名叫 Computer Systems: A Programmer's Perspective,速查之!
在2.1節的開頭提到,字節(byte)是最小可尋址單位,大多數計算機使用8位的塊。 啊,8位,那位又是什麼呢?嗯,位是一種存儲結構,一個位只能存儲0或者1。
後面2.1.2節中提到
每台計算機都有一個字長(word size),指明了整數和指針數據的 nominal size。
指針是什麼,指針就是內存中的地址啊,假如字長為w位,那麼地址的數目就是2^w個啊,那一個地址代表多大內存呢?
前面說了,字節(byte)是最小可尋址單位,所以一個地址代表一個字節。當字長是w位時,地址數目2^w個,共有2^w個字節的內存空間。
如果計算機字長為32,即傳說中的32位計算機,那麼它可以表示的內存空間就是 2^32 個字節,這就是傳說中的4G啊!
現在我們是由字長32位,也就是整數的大小32位,推出了內存空間4G。我現在在想:
是不是一開始是決定內存空間是4G,所以才定下了字長32位的規矩,由此,機器的natural size是32位, 所以,編譯器才將C語言中int類型才是32位呢?
可是我沒有證據啊!
沒有證據就嘗試推理一下吧。
我們知道32位機器是由16位機器擴展來的,那為什麼要擴展機器字長呢?這個問題原因之一,我們剛才已經解釋過了,如果不擴展,那麼機器最大尋址空間就比較小,即使我給你一個大內存,你也用不上啊。這可能這也今天我們從32位轉到64位的原因吧。
所以,現在我們明白了,由於我們想要更大的內存地址空間,所以就將字長從16位提升為32位,而字長代表著指針和整數類型的大小,所以最終整數類型就是32位了。
不過這裡還有不少問題。
字長這東西只是個抽象的概念,方便我們描述機器的一些屬性,暫時不談。
先說指針。對於機器來說,哪裡有什麼指針的概念,指針是C語言中的東西,編譯成匯編後就沒指針這個概念了。但是,指針表示的是內存的地址,而內存的地址又和機器中的什麼部件相關呢?
再說整數。到匯編這一層,整數的概念還存在嗎?整數的概念應該是和匯編中的算術指令相關,那麼算術指令又和機器中的什麼部件相關呢?
最後,指針是表示內存地址啊,我們有了更大內存,那麼內存地址需要更長的位來表示是可以理解的,可是,這關你整數什麼事啊?我內存地址32位,整數16位不行嗎?
其實,總的問題就是
字長都與機器的什麼部件相關
要解釋這個問題,我們發現自己不由自主地來到了《深入理解計算機系統》的第四章“處理器體系結構”。
這一章以一種叫Y86的處理器介紹了處理器體系結構的方方面面。首先介紹了寄存器,寄存器是一種存儲部件,存儲什麼?存儲信息,存儲信息用來做什麼呢?用來計算。我們在C語言中使用一個簡單的加法計算,在處理器這一層,就需要使用寄存器來幫助我們計算。我們把一個簡單的C語言編譯成匯編看看。
/* test_add.c */ #includeint main(void) { int a = 1; int b = 2; int c = a + b; return 0; }
使用 GCC 編譯一下
gcc -S test_add.c -o test_add.s
然後查看一下主要代碼。
movl $1, -12(%ebp) movl $2, -8(%ebp) movl -8(%ebp), %eax movl -12(%ebp), %edx addl %edx, %eax movl %eax, -4(%ebp)
其中的 ebp eax edx 就是寄存器。
可以看出,數據先放到棧裡,再從棧裡放到寄存器裡,然後再進行加法運算,最後再從寄存器裡把結果放回棧裡。
下面的圖是書中給出的一個處理器的抽象視圖:
棧是什麼?棧是一種抽象概念,這裡的棧就是指內存。
書裡說了,在32位計算機中,這些寄存器的大小就是32位。可見,
字長與寄存器大小一樣
除此之外,我們可以看到,需要計算的時候,movl 指令將數據從內存中放到寄存器裡,由於內存和寄存器是不同的部件,所以需要一個部件來傳遞數據,這種部件叫做數據總線。
寄存器的大小與字長相同,那麼這種數據總線每次能傳送的數據也應該與字長相同,所以:
字長與數據總線寬度一樣
另外,再想像一下,你想要從內存中取數據出來,總要告訴內存你取的是哪個地址的數據吧,所以,“地址”這個數據也是要從某個地方傳送到內存的。只要傳遞,就需要有部件支持,這個部件叫做地址總線,地址總線傳遞地址,地址大小與字長一樣,那麼,我們可以知道:
字長與地址總線寬度一樣
好了,到了這裡,我們的分析就差不多了,總結一下:
我們由C語言中int類型的大小,得到了字長這個概念,又從字長這個概念尋找了與其相關的一些機器部件的屬性。到現在為此,與字長相關的有:
在 Wikipedia 的 Word(computer_architecture)詞條中,我們可以看到自1837年以來,一系列計算機體系結構中與字長相關的一些屬性的變化。
我們再想想,為什麼要將這麼多種部件都設置成相同長度?我想,可能是因為計算機內部實在太復雜了,各個部件之間需要緊密地配合,共同完成復雜的任務。尤其是數據,需要在各個部件之間傳遞,如果這些部件之間大小不統一,就會增加機器的復雜度,由於,我們將這些部件大小盡可能統一,進而提出字長這種概念來描述計算機的重要性質。
到這裡,我們再想一下,字長這個概念和這麼多部件相關,那麼確定字長多大應該不僅僅與內存大小有關系。比如字長代表寄存器的大小,寄存器與機器的運算直接相關,字長變大後,每次能參與計算的值也相應變大,以前我們計算兩個很大的數的和時,可能需要動用好幾個寄存器,現在咱字長大了,寄存器也大了,只需要兩個寄存器就可以了。
由此可見,字長的確定是一個綜合的考量,代表著計算機計算,存儲能力的全面提升。
文章結束了,思考永不停止。