天天看點

《C語言程式設計:問題與求解方法》——1.3節提高部分

本節書摘來自華章社群《c語言程式設計:問題與求解方法》一書中的第1章,第1.3節提高部分,作者:何 勤,更多章節内容可以通路雲栖社群“華章社群”公衆号檢視

1.3 提高部分

1.3.1 程式(指令序列)在硬體上的運作過程

任何一條指令的執行過程都可以分為以下三個階段:

1)取指令階段(類似于理想廚房通過三條傳送帶到冰箱中取一個加工步驟):控制單元負責根據pc寄存器中的值,通過三條總線的密切協作,從記憶體中取得一條指令,并将其儲存到ir寄存器中;同時将pc寄存器中的原來值加上剛取到的這條指令的位元組數,将結果重新存入pc寄存器—這是為順序取得記憶體中的下一條指令做準備。

2)譯碼階段:控制單元對取到ir寄存器中的指令進行譯碼。

3)執行階段:控制單元根據譯碼後得到的指令的操作要求,通過總線釋出指令,要求相關部件執行這條指令。一條指令到了執行階段,才發揮出了指令計算機硬體進行相關操作的作用。

任何一條指令在取指令階段和譯碼階段都是完全類似的、由硬體主動進行的。但是在指令的執行階段,硬體是在由程式員編寫的指令(注意:這是由人編寫出來的軟體)的指令下被動進行的工作的。而且,由于指令類型的不同,參與執行指令操作的部件和任務是不同的。

一條指令執行完畢後,cpu立即自動地順序取得并執行下一條指令。這樣形成了一個永不停息的(隻要不出現嚴重故障、斷電或停機)、自動化的(通常按照順序)讀取指令—執行指令的硬體與軟體密切協作、交替配合的周期性工作。

1.3.2 結構化、規範化的機器語言程式

1.主程式和子程式

對于計算機來說,如果正在執行的指令所在的記憶體位址是m的話,下一條要執行的指令一定是存放在記憶體位址為m+l的記憶體單元中(l是正在執行的指令的位元組數)——除非剛剛執行的是一條跳轉指令。

計算機中的跳轉指令與理想廚房中的跳轉指令的作用完全類似。

标準的、結構化的機器語言程式在記憶體中通常都是分段連續的,每一個連續存放在記憶體中的程式段都是功能相對獨立的,這種程式段稱為子程式(或稱為子程式段)。這些程式段中還有一個唯一的、特殊的程式段—主程式(或稱為主程式段)。

程式都是從主程式段的第一條指令開始運作的,通過主程式段中的第i條(有條件)跳轉指令,可以(在條件成立時)跳轉到别的子程式段中的第一條指令,然後順序往下運作;子程式段中的最後一條指令也是一條跳轉指令,這條指令将跳轉重新回到主程式段中的第i+1條指令。

延伸與拓展:每個程式段内部也可以有跳轉指令,這些指令在結構化的(機器語言)程式中常常是用來構成選擇結構和循環結構的,請參見第4章和第5章。

2.“調用”與“傳回”

在程式執行過程中,從主程式段跳轉到子程式段稱為調用(call),子程式段運作結束後跳回到主程式段稱為傳回(return)。

由于各個程式段可能是由不同的人在不同時期編寫的,子程式的調用和傳回過程中通常都存在着兩個程式段之間的資料傳遞和儲存現場、恢複現場的問題,這些問題将在後面章節中進行讨論。

1.3.3 各種數制之間的轉換

1.二進制數轉換為十進制數

任意一個二進制整數,比如二進制整數1011,其數值可用以下展開式來表示:

此二進制數的值等于十進制的1×8+0×4+1×2+1×1=8+2+1=11。(2)

由此可以得到二進制整數轉化成十進制整數的一般方法:隻要将一個二進制整數(比如1011)展開後的(2)式中的每一位的系數值,(采用十進制乘法規則)乘以這一位轉化成十進制數後的權重(即2的幾次方),然後再将逐個乘積項的數值(采用十進制加法規則)相加起來即得到十進制數值。

由此可得到任意r進制實數(或整數)的表示法。一般情況下,任何一個r進制實數,都可以緊湊地表示為:

在計算科學中,r通常是2、8、10、16中的某個正整數。

其中的任何一個(系數)ri都是0到r–1之間的一個正整數,r0是個位數,r–1是小數點後的第一位數,rn是最高位數。r–m是小數點後的最低位數。如果小數點後的所有(系數)r–1、r–2、...、r–m等都為0,則(3)式表示一個整數。

将任意一個r進制數擴大r倍(即乘以r),隻需将小數點右移一位即可。類似地,将任意一個r進制數縮小r倍(即除以r),隻需将小數點左移一位即可。

r等于10時,就是人們最為熟悉的十進制數,這種(可以帶小數點的)十進制數的簡潔緊湊表示方法,是由古代印度人發明的(來源于人有十根手指)。

任何一個r進制實數,都可以用多項式展開表示為:

±(rn×rn+rn?×rn?+…+r1×r1+r0×r0+r?×r?+r?×r?+…+r杕×r杕)(ri稱為系數,ri稱為權重)

在r進制數值的多項式展開表示法中,不使用小數點。

2.将十進制整數轉化成二進制數

将十進制數轉換成二進制數一般使用短除法。具體步驟為:把一個十進制轉整數(比如89)換成二進制,隻需用新的基數2(采用十進制除法規則)除以這個十進制數(89),餘數(1)是結果左邊的下一位數字,商(44)是新的被除數,然後重複這個過程,直到商為0時終止。

短除法就是按照這個過程,把要轉換的十進制整數不斷除以2,然後取餘數,商作為新的被除數,直到商為0的時候結束。然後把餘數倒着寫出來。

例如,把89轉換成二進制數的過程如下:

最後得到(89)10=(1011001)2。

注意:-短除法也完全适用于将一個十進制整數轉換為一個任意r(r>2)進制的整數,隻需将除數由2替換為r即可。

3.将十進制小數轉化成二進制小數

将十進制小數轉換為二進制小數的過程,與整數的進制轉換過程有些類似,但計算方法恰恰相反:不是用新的基數2除這個數,而是用新基數2去乘它。乘法的進位(進到個位的數字)将成為答案右邊的下一位數字,乘法結果中的小數部分将成為新的被乘數,整個過程直到乘法結果中的小數部分為0時終止。

( 要想明白這種轉換規則的道理,請考慮恒等式: 0.375=r–1×2–1+r–2×2–2+…+r–m×2杕……中的系數r–1、r–2等應該如何通過每次乘2來求得。)

例如,把十進制小數0.375轉換成二進制小數:

是以,(0.375)10=(0.011)2。

4.二進制整數轉換成十六進制整數

雖然計算機用二進制存儲和傳輸各種資料,但是它并不适合在計算機外部(比如在源程式中)表示資料。因為與十進制資料相比,二進制資料(即位串)過于長了。

然而,十進制又不像二進制那樣能夠直接顯示存儲在計算機中的是什麼。在二進制位串和十進制數字之間不存在明顯的關系,它們之間的轉換很不友善。

為此,人們想到了使用十六進制(或八進制)。在計算機外部用十六進制來表示計算機内部的二進制數,由于二進制和十六進制數之間的轉換非常快捷,這樣就能比較好地解決以上難題。

十六進制數用16個符号來表示一個數,字元集是{0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f}。注意,符号a,b,c,d,e,f(也可用小寫)的值分别等于十進制的10,11,12,13,14,15。

表1-3是二進制、八進制、十六進制整數對照表。利用此表就能很快實作二進制和十六進制數之間的轉換。

表1-3 二進制、八進制、十六進制整數對照表

《C語言程式設計:問題與求解方法》——1.3節提高部分

下面介紹二進制整數與十六進制整數(或八進制)之間是如何轉換的。

一個二進制數,比如:

(1011110)2=1×26+0×25+1×24+1×23+1×22+1×21+0×20 //展開

=(0×23+1×22+0×21+1×20)×24+(1×23+1×22+1×21+0×20)×20 //每4個分為一組

=(101)2×24+(1110)2×20

=5×161+e×160 (将圓括号中的數經過查表1-3轉換為十六進制數)

=(5e)16

也就是說,隻要将一個二進制數,從最低位開始,每4個二進制的位通過查表1-3,直接轉變成十六進制的1位數。

由此可見,二進制整數轉換成十六進制整數是很友善快捷的,隻要直接查找表1-3即可,不需任何轉換計算。我們可以看到,一個較長的二進制數轉換成十六進制數後确實簡短多了。

二進制整數轉換成八進制整數與此完全類似。

5.十六進制整數轉換成二進制整數

由于上述二進制整數轉換成十六進制整數的整個過程完全是可逆的。是以,完全可以通過查表1-3,将一個十六進制轉化成二進制。例如,對于十六進制e5,通過查表可知,十六進制的e對應1110,十六進制的5對應0101。

是以,十六進制數e5轉化為二進制數的過程為:

結果為:(e5)16=(11100101)2。

注意:每個十六進制數字都轉化成4位二進制數字,不足4位要補足成4位。比如,十六進制5對應着二進制0101而不是對應着二進制101。

繼續閱讀