(四)羽夏看C語言——循環與跳轉


寫在前面

  此系列是本人一個字一個字碼出來的,包括示例和實驗截圖。本人非計算機專業,可能對本教程涉及的事物沒有了解的足夠深入,如有錯誤,歡迎批評指正。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閑錢,可以打賞支持我的創作。如想轉載,請把我的轉載信息附在文章后面,並聲明我的個人信息和本人博客地址即可,但必須事先通知我

你如果是從中間插過來看的,請仔細閱讀 (一)羽夏看C語言——簡述 ,方便學習本教程。

if語句

  生活中,經常會有選擇或者情況需要自己判斷,計算機也是如此。所有的判斷語句還是后面將要介紹的循環其實都是由JCC指令組成的。我們先給出如下代碼示例:

#include <iostream>
using namespace std;
//如果是C,請自行將頭文件包含改為 stdio.h 和 stdlib.h
//將 cout 改為 printf_s(可以 printf,但微軟編譯器編譯會報錯,自行科普)

int main()
{
    int c = 0;
    int re = 0;
    cout << "請輸入數字:" << endl;
    cin >> c;

    if (c==0)
    {
        re = -c;
    }
    else if (c==1)
    {
        re = c + c;
    }
    else if (c==2)
    {
        re = 4;
    }
    else if (c==3)
    {
        re = c * c;
    }
    else if (c==4)
    {
        re = c + c + 5;
    }
    else if (c==5)
    {
        re = c;
    }
    else
    {
        re = -1;
    }

    system("pause");
    return 0;
}

  然后查看一下它的反匯編:

  是不是很簡單粗暴,每次需要判斷是不是,不是再跳轉,雖然結構清晰,但生成了大量的匯編代碼,影響效率,寫起來也挺費勁。

switch語句

  switch語句在多情況判斷上是用的最多的,是if語句的升級版,絕大多數情況比單純的if-else高效的多,下面我們用代碼揭開它神秘的面紗:

#include <iostream>
using namespace std;
//如果是C,請自行將頭文件包含改為 stdio.h 和 stdlib.h
//將 cout 改為 printf_s(可以 printf,但微軟編譯器編譯會報錯,自行科普)
//將 cin 改為 scanf_s(可以 scanf,但微軟編譯器編譯會報錯,自行科普)

int main()
{
    int c = 0;
    int re = 0;
    cout << "請輸入數字:" << endl;
    cin >> c;
    switch (c)
    {
        case 0:
            re = -c;
            break;
        case 1:
            re = c + c;
            break;
        case 2:
            re = 4;
            break;
        case 3:
            re = c * c;
            break;
        case 4:
            re = c + c + 5;
            break;
        case 5:
            re = c;
            break;
        default:
            re = -1;
            break;
    }

    system("pause");
    return 0;
}

  然后我們查看它的反匯編:

  讓我們分析一下比較有意思的反匯編:
mov eax,dword ptr [ebp-0Ch]
mov dword ptr [ebp+FFFFFF20h],eax
cmp dword ptr [ebp+FFFFFF20h],5
ja 0047255F
mov ecx,dword ptr [ebp+FFFFFF20h]
jmp dword ptr [ecx*4+004725C8h]

  ebp-0Ch就是c的地址,它先比較這個東西是否大於5,如果大於直接到轉到0x0047255F這個地址,也就是default語句,看來編譯器還是挺“聰明的”。然而最“聰明”的不在這里,而是jmp dword ptr [ecx*4+004725C8h]這句匯編。讓我們看看0x04725C8這個地址到底存儲的是什么東西:

  首先打開內存窗口,輸入那個地址,然后在內存窗口顯示右鍵選中四個字節整數沒有文本十六進制顯示即可。得到如下圖結果:

  如果你細心的話,你會發現這里面存儲的都是每個case的地址,被稱為地址表。我只需計算出一次結果,就可以跳轉到我需要的位置。

  咱們舉的例子是情況連續的時候,如果不連續但差距不算太大呢,我們嘗試把case 3刪掉,看看有什么情況出現。

- 反匯編 -

- 地址表 -

  可以看出表的成員個數不變,但被刪除的case的地址處被填充了default語句的地址。編譯器可以通過某種推斷來實現地址表的構建提高運行效率,但是如果每個case沒有任何規律可言的話,那會怎么樣呢?

#include <iostream>
using namespace std;
//如果是C,請自行將頭文件包含改為 stdio.h 和 stdlib.h
//將 cout 改為 printf_s(可以 printf,但微軟編譯器編譯會報錯,自行科普)
//將 cin 改為 scanf_s(可以 scanf,但微軟編譯器編譯會報錯,自行科普)

int main()
{
    int c = 0;
    int re = 0;
    cout << "請輸入數字:" << endl;
    cin >> c;
    switch (c)
    {
        case 0:
            re = -c;
            break;
        case 15:
            re = c + c;
            break;
        case 200:
            re = 4;
            break;
        case 489:
            re = c + c + 5;
            break;
        case 542:
            re = c;
            break;
        default:
            re = -1;
            break;
    }

    system("pause");
    return 0;
}

  然后看一下反匯編:

  哈哈,這回編譯器“找不到頭腦了”,只能老老實實的用if-else的樣式生成匯編了。

循環語句

  循環語句應該是編程中經常會用到的語句。所有的形式示例如下:

for語句

for (int i = 0; i < 5; i++)
{
    //do something
}

while語句

int i;
do
{
    //do something
} while (i<5);

do語句

int i;
while (i<5)
{
    //do something
}

  在匯編層面,所有循環到匯編的本質都是一樣的,下面我們用代碼進行驗證:

#include <iostream>
//如果是C,請自行將頭文件包含改為 stdio.h 和 stdlib.h

int main()
{
    int c = 0;

    //for循環
    for (int i = 0; i < 5; i++)
    {
        c++;
    }

    //do循環
    int i = 0;
    do
    {
        c++;
        i++;
    } while (i < 5);

    //while循環
    i = 0;
    while (i < 5)
    {
        c++;
        i++;
    }

    system("pause");
    return 0;
}

  然后編譯運行,查看它的反匯編,結果如下:

- for循環 -

- do循環 -

- while循環 -

跳轉語句

  breakcontinuegoto被我統稱為跳轉語句。breakcontinue語句經常在循環語句和switch語句出現,經常和if配套以判斷是否不符合循環條件跳出而使用。翻譯到匯編層面,它不過就是一條jmp指令,switch語句的已經體現了。goto語句翻譯到匯編也是一條jmp指令,但如果處理不善,就會打亂程序執行流程出現不太可預測的結果,不太建議使用。那我們做一個循環語句的,其他自行探索實驗,代碼如下:

#include <iostream>
using namespace std;
//如果是C,請自行將頭文件包含改為 stdio.h 和 stdlib.h
//將 cout 改為 printf_s(可以 printf,但微軟編譯器編譯會報錯,自行科普)

int main()
{
    for (int i = 0; i < 10; i++)
    {
label:
        if (i==2)
            continue;

        if (i == 7)
            goto label;

        if (i==8)
            break;
    }
    system("pause");
    return 0;
}

for each語句

  經查閱,這個語句僅在微軟的編譯器里面有。所以本人還是略微做一下實驗,來看看for each語句到底為我們做了什么東西。在實驗之前,需要通過項目屬性頁-C/C++-語言來關閉符合模式,代碼如下:

#include <iostream>
using namespace std;
//如果是C,請自行將頭文件包含改為 stdio.h 和 stdlib.h
//將 cout 改為 printf_s(可以 printf,但微軟編譯器編譯會報錯,自行科普)

int main()
{
    int nums[] = { 1,2,3,4,5,6 };
    int num = 0;
    for each (int var in nums)
    {
        num += var;
    }
    system("pause");
    return 0;
}

  然后看一下反匯編:

  一個簡簡單單的for each卻為我們生成了好幾行代碼,剩下的還請自行探索。

下一篇

  (五)羽夏看C語言——結構體與類(C++)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM