順序、條件、循環語句的底層解釋

順序結構

數據傳送指令

    咱們都清楚,絕大多數編譯器都把彙編語言做爲中間語言,把彙編語言程序變成可運行的二進制文件早就解決了,因此如今的高級語言基本上只須要把本身翻譯成彙編語言就能夠了。git

    彙編指令總共只有那麼多,大多數指令都是對數據進行操做,好比常見的數據傳送指令mov。不難理解,被操做數據無非有三種形式,當即數,即用來表示常數值;寄存器,此時的數據即存放在指定寄存器中的內容;內存引用,它會根據計算出來的地址訪問某個內存位置。github

    須要注意的是,到了彙編層級,就不像高級語言那樣隨隨便便int就能和long類型的數據相加減,他們在底層所佔有的字節是不同的,彙編指令是區分操做數據大小的,好比數據傳送指令,就有下面這些品種(x86-64 對數據傳送指令加了一條限制:兩個操做數不能都指向內存位置)。面試

image

壓棧與彈棧

    對於棧,我想沒必要多講,IT 行業的同窗都清楚,它是一種線性數據結構,其中的數據遵循「先進後出」原則,寄存器%rsp保存着棧頂元素的地址,即棧頂指針。一個程序要運行起來,離不開棧這種數據結構。數組

    棧使用最多的就是彈棧popq和壓棧pushq操做。好比將一個四字值壓入棧中,棧頂指針首先要減 8(棧向下增加),而後將值寫到新的棧頂地址;而彈棧則須要先將棧頂數據讀出,而後再將棧指針加 8。因此pushqpopq指令就能夠表示爲下面的形式。數據結構

// 壓棧
subq $8, %rsp
movq %rbp, (%rsp)

// 彈棧
movq (%rsp), %rax
addq $8, %rsp
複製代碼

    其餘還有算術、邏輯、加載有效地址、移位等等指令,能夠查閱相關文檔瞭解,不做過多介紹,彙編看起來確實枯燥乏味。架構

條件結構

    前面講的都是順序結構,咱們的程序中不可能只有順序結構,條件結構是必不可缺的元素,那麼彙編又是如何實現條件結構的呢?oop

    首先你須要知道,除了整數寄存器,CPU 還維護着一組條件碼寄存器,咱們主要是瞭解如何把高級語言的條件結構轉換爲彙編語言,不去關注這些條件碼寄存器,只須要知道彙編能夠經過檢測這些寄存器來執行條件分支指令。post

if-else 語句

    下面是 C 語言中的if-else語句的通用形式。性能

if(test-expr){
    then-statement
}else{
    else-statement
}
複製代碼

    彙編語言一般會將上面的 C 語言模板轉換爲下面的控制流形式,只要使用條件跳轉和無條件跳轉,這種形式的控制流就能夠和彙編代碼一一對應,咱們以 C 語言形式給出。測試

t = test-expr;
    if(!t){
        goto false;
    }
    then-statement;
    goto done;
false:
    else-statement;
done:
複製代碼

    可是這種條件控制轉移形式的代碼在現代處理器上可能會很低效。緣由是它沒法事先肯定要跳轉到哪一個分支,咱們的處理器經過流水線來得到高性能,流水線的要求就是事先明確要執行的指令順序,而這種形式的代碼只有當條件分支求值完成後,才能決定走哪個分支。即便處理器採用了很是精密的分支預測邏輯,可是仍是有錯誤預測的狀況,一旦預測錯誤,那將會浪費 15 ~ 30 個時鐘週期,致使性能降低。

    在流水線中,把一條指令分爲多個階段,每一個階段只執行所需操做的一小部分,好比取指令、肯定指令類型、讀數據、運算、寫數據以及更新程序計數器。流水線經過重疊連續指令的步驟來得到高性能,好比在取一條指令的同時,執行它前面指令的算術運算。因此若是事先不知道指令執行順序,那麼事先所作的預備工做就白乾了。

    爲了提升性能,能夠改寫成使用條件數據傳送的代碼,好比下面的例子。

v = test-expr ? then-expr : else-expr;

// 使用條件數據傳送方法
v = then-expr;
ve = else-expr;
t = test-expr;
if(!t){
    v = ve;
}
複製代碼

    這樣改寫,就能提升程序的性能了,可是並非全部的條件表達式均可以使用條件傳送來編譯,通常只有當兩個表達式都很容易計算時,編譯器纔會採用條件數據傳送的方式,大部分都仍是使用條件控制轉移方式編譯。

switch 語句

    switch語句能夠根據一個整數索引值進行多重分支,在處理具備多種可能結果的測試時,這種語句特別有用。爲了讓switch的實現更加高效,使用了一種叫作跳轉表的數據結構(Radis 也是用的跳錶)。跳轉表是一個數組,表項 i 是一個代碼段的地址,當開關狀況數量比較多的時候,就會使用跳轉表。

    咱們舉個例子,仍是採用 C 語言的形式表是控制流,要理解的是執行switch語句的關鍵步驟就是經過跳轉表來訪問代碼的位置。

void switch_eg(long x, long n, long *dest){
    long val = x;
    switch(n){
        case 100:
            val *= 13;
            break;
        case 102:
            val += 10;
        case 103:
            val += 11;
            break;
        case 104:
        case 105:
            val *= val;
            break;
        default:
            val = 0;
    }
    *dest = val;
}
複製代碼

    要注意的是,上面的代碼中有的分支沒有break,這種問題在筆試中會常常遇到,沒有break會繼續執行下面的語句,即變成了順序執行。上面的代碼會被翻譯爲下面這種控制流。

void switch_eg(long x, long n, long *dest){
        static void *jt[7] = {
            &&loc_A, &&loc_def, &&loc_B,
            &&loc_C, &&loc_D, &&loc_def,
            &&loc_D
        };
        unsigned long index = n - 100;
        long val;
        if(index > 6){
            goto loc_def;
        }
        goto *jt[index];
    loc_A:
        val = x * 13;
        goto done;
    loc_B:
        x = x + 10;
    loc_C:
        val = x + 11;
        goto done;
    loc_D:
        val = x * x;
        goto done;
    loc_def:
        val = 0;
    done:
        *dest = val;
}
複製代碼

循環結構

    C 語言中有do-whilewhilefor三種循環結構,它們的通用形式通常都長下面那樣。

// do-while
do
    body-statement while(test-expr);
    
// while
while(test-expr)
    body-statement
    
// for
for(init-expr; test-expr; update-expr)
    body-statement
複製代碼

    do-while的特色是body-statement必定會執行一次,因此咱們能夠將do-while翻譯成下面的控制流形式,很容易就能聯想到它的彙編形式。

loop:
    body-statement;
    t = test-expr;
    if(t){
        goto loop;
    }
複製代碼

    while循環咱們給出兩種形式的控制流,其中一種包含do-while形式,以下所示。

// 第一種形式
t = test-expr;
if(!t){
    goto done;
}
do
    body-statement;
    while(test-expr);
done:


// 第二種形式
    goto test;
loop:
    body-statement;
test:
    t = test-expr;
    if(t){
        goto loop;
    }
複製代碼

    面試的時候,有的面試官會問你for循環的執行順序,如今深刻理解了三種循環的機制,不再怕面試官啦。for循環能夠轉換成以下的while形式。

init-expr;
while(test-expr){
    body-statement;
    update-expr;
}
複製代碼

    有了這種形式的for循環,咱們只須要將其中的while部分再翻譯一下就行了,前文給出了兩種while翻譯的方式,而具體採用哪一種方式,取決於編譯器優化的等級。

總結

    計算機就是用那麼幾條簡簡單單的指令就完成了各類複雜的操做,不得不折服於計算機科學家們的魅力。如今人工智能被炒的很火熱,而後人是事件、情感驅動的,而計算機是控制流驅動的,因此從架構上就決定了,馮諾依曼體系計算機實現的都是弱人工智能。

相關文章
相關標籤/搜索