• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            興海北路

            ---男兒仗劍自橫行
            <2008年3月>
            2425262728291
            2345678
            9101112131415
            16171819202122
            23242526272829
            303112345

            統(tǒng)計(jì)

            • 隨筆 - 85
            • 文章 - 0
            • 評(píng)論 - 17
            • 引用 - 0

            常用鏈接

            留言簿(6)

            隨筆分類

            隨筆檔案

            收藏夾

            全是知識(shí)啊

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            GCC編譯背后(第一部分:預(yù)處理和編譯)
            by falcon <zhangjinw@gmail.com>

            平時(shí)在Linux下寫(xiě)代碼,直接用"gcc -o out in.c"就把代碼編譯好了,但是這后面到底做了什么事情呢?如果學(xué)習(xí)過(guò)編譯原理則不難理解,一般高級(jí)語(yǔ)言程序編譯的過(guò)程莫過(guò)于:預(yù)處理、編譯、匯編、鏈 接。gcc在后臺(tái)實(shí)際上也經(jīng)歷了這幾個(gè)過(guò)程,我們可以通過(guò)-v參數(shù)查看它的編譯細(xì)節(jié),如果想看某個(gè)具體的編譯過(guò)程,則可以分別使用-E,-S,-c和- O,對(duì)應(yīng)的后臺(tái)工具則分別為cpp,cc1,as,ld。下面我們將逐步分析這幾個(gè)過(guò)程以及相關(guān)的內(nèi)容,諸如語(yǔ)法檢查、代碼調(diào)試、匯編語(yǔ)言等。

            1、預(yù)處理

                開(kāi)篇簡(jiǎn)述:預(yù)處理是C語(yǔ)言程序從源代碼變成可執(zhí)行程序的第一步,主要是C語(yǔ)言編譯器對(duì)各種預(yù)處理命令進(jìn)行處理,包括頭文件的包含、宏定義的擴(kuò)展、條件編譯的選擇等。

                以前沒(méi)怎么“深入”預(yù)處理,腦子對(duì)這些東西總是很模糊,只記得在編譯的基本過(guò)程(詞法分析、語(yǔ)法分析)之前還需要對(duì)源代碼中的宏定義、文件包含、條件編譯 等命令進(jìn)行處理。這三類的指令很常見(jiàn),主要有#define, #include和#ifdef ... #endif,要特別地注意它們的用法。(更多預(yù)處理的指令請(qǐng)查閱相關(guān)資料)

                #define除了可以獨(dú)立使用以便靈活設(shè)置一些參數(shù)外,還常常和#ifdef ... #endif結(jié)合使用,以便靈活地控制代碼塊的編譯與否,也可以用來(lái)避免同一個(gè)頭文件的多次包含。關(guān)于#include貌似比較簡(jiǎn)單,通過(guò)man找到某個(gè) 函數(shù)的頭文件,copy進(jìn)去,加上<>就okay。這里雖然只關(guān)心一些技巧,不過(guò)預(yù)處理還是蘊(yùn)含著很多潛在的陷阱(可參考<C Traps & Pitfalls>),我們也需要注意的。下面僅介紹和預(yù)處理相關(guān)的幾個(gè)簡(jiǎn)單內(nèi)容。

          1. 打印出預(yù)處理之后的結(jié)果:gcc -E hello.c

                這樣我們就可以看到源代碼中的各種預(yù)處理命令是如何被解釋的,從而方便理解和查錯(cuò)。

                實(shí)際上gcc在這里是調(diào)用了cpp的(雖然我們通過(guò)gcc的-v僅看到cc1),cpp即The C Preprocessor,主要用來(lái)預(yù)處理宏定義、文件包含、條件編譯等。下面介紹它的一個(gè)比較重要的選項(xiàng)-D。

          2. 在命令行定義宏:gcc -Dmacro hello.c

                這個(gè)等同于在文件的開(kāi)頭定義宏,即#define maco,但是在命令行定義更靈活。例如,在源代碼中有這些語(yǔ)句。
            #ifdef DEBUG
            printf("this code is for debugging\n");
            #endif

                如果編譯時(shí)加上-DDEBUG選項(xiàng),那么編譯器就會(huì)把printf所在的行編譯進(jìn)目標(biāo)代碼,從而方便地跟蹤該位置的某些程序狀態(tài)。這樣-DDEBUG就可以當(dāng)作一個(gè)調(diào)試開(kāi)關(guān),編譯時(shí)加上它就可以用來(lái)打印調(diào)試信息,發(fā)布時(shí)則可以通過(guò)去掉該編譯選項(xiàng)把調(diào)試信息去掉。

            本節(jié)參考資料:
            [1] C語(yǔ)言教程第九章:預(yù)處理
            http://www.bc-cn.net/Article/kfyy/cyy/jc/200409/9.html
            [2] 更多
            http://www.hemee.com/kfyy/c/6626.html
            http://www.91linux.com/html/article/program/cpp/20071203/8745.html
            http://www.janker.org/bbs/programmer/2006-10-13/327.html

            2、編譯(翻譯)

                開(kāi)篇簡(jiǎn)要:編譯之前,C語(yǔ)言編譯器會(huì)進(jìn)行詞法分析、語(yǔ)法分析(-fsyntax-only),接著會(huì)把源代碼翻譯成中間語(yǔ)言,即匯編語(yǔ)言。如果想看到這個(gè) 中間結(jié)果,可以用-S選項(xiàng)。需要提到的是,諸如shell等解釋語(yǔ)言也會(huì)經(jīng)歷一個(gè)詞法分析和語(yǔ)法分析的階段,不過(guò)之后并不會(huì)進(jìn)行“翻譯”,而是“解釋”, 邊解釋邊執(zhí)行。

                把源代碼翻譯成匯編語(yǔ)言,實(shí)際上是編譯的整個(gè)過(guò)程中的第一個(gè)階段,之后的階段和匯編語(yǔ)言的開(kāi)發(fā)過(guò)程沒(méi)有什么區(qū)別。這個(gè)階段涉及到對(duì)源代碼的詞法分析、語(yǔ)法檢查(通過(guò)-std指定遵循哪個(gè)標(biāo)準(zhǔn)),并根據(jù)優(yōu)化(-O)要求進(jìn)行翻譯成匯編語(yǔ)言的動(dòng)作。

                如果僅僅希望進(jìn)行語(yǔ)法檢查,可以用-fsyntax-only選項(xiàng);而為了使代碼有比較好的移植性,避免使用gcc的一些特性,可以結(jié)合-std和- pedantic(或者-pedantic-erros)選項(xiàng)讓源代碼遵循某個(gè)C語(yǔ)言標(biāo)準(zhǔn)的語(yǔ)法。這里演示一個(gè)簡(jiǎn)單的例子。

            Quote:

            $ cat hello.c
            #include <stdio.h>
            int main()
            {
                    printf("hello, world\n")
                    return 0;
            }
            $ gcc -fsyntax-only hello.c
            hello.c: In function ‘main’:
            hello.c:5: error: expected ‘;’ before ‘return’
            $ vim hello.c
            $ cat hello.c
            #include <stdio.h>
            int main()
            {
                    printf("hello, world\n");
                    int i;
                    return 0;
            }
            $ gcc -std=c89 -pedantic-errors hello.c    #默認(rèn)情況下,gcc是允許在程序中間聲明變量的,但是turboc就不支持
            hello.c: In function ‘main’:
            hello.c:5: error: ISO C90 forbids mixed declarations and code



                語(yǔ)法錯(cuò)誤是程序開(kāi)發(fā)過(guò)程中難以避免的錯(cuò)誤(人的大腦在很多條件下都容易開(kāi)小差),不過(guò)編譯器往往能夠通過(guò)語(yǔ)法檢查快速發(fā)現(xiàn)這些錯(cuò)誤,并準(zhǔn)確地告訴你語(yǔ)法錯(cuò) 誤的大概位置。因此,作為開(kāi)發(fā)人員,要做的事情不是“恐慌”(不知所措),而是認(rèn)真閱讀編譯器的提示,根據(jù)平時(shí)積累的經(jīng)驗(yàn)(最好在大腦中存一份常見(jiàn)語(yǔ)法錯(cuò) 誤索引,很多資料都提供了常見(jiàn)語(yǔ)法錯(cuò)誤列表,如<C Traps&Pitfalls>和最后面的參考資料[12]也列出了很多常見(jiàn)問(wèn)題)和編輯器提供的語(yǔ)法檢查功能(語(yǔ)法加亮、括號(hào)匹配提示 等)快速定位語(yǔ)法出錯(cuò)的位置并進(jìn)行修改。

                語(yǔ)法檢查之后就是翻譯動(dòng)作,gcc提供了一個(gè)優(yōu)化選項(xiàng)-O,以便根據(jù)不同的運(yùn)行平臺(tái)和用戶要求產(chǎn)生經(jīng)過(guò)優(yōu)化的匯編代碼。例如,

            Quote:

            $ gcc -o hello hello.c            #采用默認(rèn)選項(xiàng),不優(yōu)化
            $ gcc -O2 -o hello2 hello.c        #優(yōu)化等次是2
            $ gcc -Os -o hellos hello.c        #優(yōu)化目標(biāo)代碼的大小
            $ ls -S hello hello2 hellos        #可以看到,hellos比較小,hello2比較大
            hello2  hello  hellos
            $ time ./hello
            hello, world

            real    0m0.001s
            user    0m0.000s
            sys     0m0.000s
            $ time ./hello2                #可能是代碼比較少的緣故,執(zhí)行效率看上去不是很明顯
            hello, world

            real    0m0.001s
            user    0m0.000s
            sys     0m0.000s

            $ time ./hellos                #雖然目標(biāo)代碼小了,但是執(zhí)行效率慢了些
            hello, world

            real    0m0.002s
            user    0m0.000s
            sys     0m0.000s



                根據(jù)上面的簡(jiǎn)單演示,可以看出gcc有很多不同的優(yōu)化選項(xiàng),主要看用戶的需求了,目標(biāo)代碼的大小和效率之間貌似存在一個(gè)“糾纏”,需要開(kāi)發(fā)人員自己權(quán)衡。

                下面我們通過(guò)-S選項(xiàng)來(lái)看看編譯出來(lái)的中間結(jié)果,匯編語(yǔ)言,還是以之前那個(gè)hello.c為例。
            Quote:

            $ gcc -S hello.c        #默認(rèn)輸出是hello.s,可自己指定,輸出到屏幕-o -,輸出到其他文件-o file
            $ cat hello.s
            cat hello.s
                    .file   "hello.c"
                    .section        .rodata
            .LC0:
                    .string "hello, world"
                    .text
            .globl main
                    .type   main, @function
            main:
                    leal    4(%esp), %ecx
                    andl    $-16, %esp
                    pushl   -4(%ecx)
                    pushl   %ebp
                    movl    %esp, %ebp
                    pushl   %ecx
                    subl    $4, %esp
                    movl    $.LC0, (%esp)
                    call    puts
                    movl    $0, %eax
                    addl    $4, %esp
                    popl    %ecx
                    popl    %ebp
                    leal    -4(%ecx), %esp
                    ret
                    .size   main, .-main
                    .ident  "GCC: (GNU) 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)"
                    .section        .note.GNU-stack,"",@progbits



                不知道看出來(lái)沒(méi)?和我們?cè)谡n堂里學(xué)的intel的匯編語(yǔ)法不太一樣,這里用的是AT&T語(yǔ)法格式。如果之前沒(méi)接觸過(guò)AT&T的,可以看看 參考資料[2]。如果想學(xué)習(xí)Linux下的匯編語(yǔ)言開(kāi)發(fā),從下一節(jié)開(kāi)始哦,下一節(jié)開(kāi)始的所有章節(jié)基本上覆蓋了Linux下匯編語(yǔ)言開(kāi)發(fā)的一般過(guò)程,不過(guò)這 里不介紹匯編語(yǔ)言語(yǔ)法。

                這里需要補(bǔ)充的是,在寫(xiě)C語(yǔ)言代碼時(shí),如果能夠?qū)幾g器比較熟悉(工作原理和一些細(xì)節(jié))的話,可能會(huì)很有幫助。包括這里的優(yōu)化選項(xiàng)(有些優(yōu)化選項(xiàng)可能在匯 編時(shí)采用)和可能的優(yōu)化措施,例如字節(jié)對(duì)齊(可以看看這本書(shū)"Linux_Assembly_Language_Programming"的第六小節(jié))、 條件分支語(yǔ)句裁減(刪除一些明顯分支)等。

            本節(jié)參考資料

            [1] Guide to Assembly Language Programming in Linux(pdf教程,社區(qū)有下載)
            http://oss.lzu.edu.cn/modules/wfdownloads/singlefile.php?cid=5&lid=94
            [2] Linux匯編語(yǔ)言開(kāi)發(fā)指南(在線):
            http://www.ibm.com/developerworks/cn/linux/l-assembly/index.html
            [3] PowerPC 匯編
            http://www.ibm.com/developerworks/cn/linux/hardware/ppc/assembly/index.html
            [4] 用于 Power 體系結(jié)構(gòu)的匯編語(yǔ)言
            http://www.ibm.com/developerworks/cn/linux/l-powasm1.html
            [5] Linux Assembly HOWTO
            http://mirror.lzu.edu.cn/tldp/HOWTO/Assembly-HOWTO/
            [6] Linux 中 x86 的內(nèi)聯(lián)匯編
            http://www.ibm.com/developerworks/cn/linux/sdk/assemble/inline/index.html
            [7] Linux Assembly Language Programming
            http://mirror.lzu.edu.cn/doc/incoming/ebooks/linux-unix/Linux_EN_Original_Books

          3. posted on 2008-03-14 15:22 隨意門 閱讀(901) 評(píng)論(0)  編輯 收藏 引用


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            国产成人久久精品二区三区| 理论片午午伦夜理片久久| 久久久久女教师免费一区| 国产午夜久久影院| 久久Av无码精品人妻系列| 久久天天躁夜夜躁狠狠| 亚洲精品综合久久| 亚洲а∨天堂久久精品| 欧美久久久久久午夜精品| 国产精品伊人久久伊人电影 | 久久国产免费| 久久青青草原精品影院| 青青草国产成人久久91网| 国产—久久香蕉国产线看观看| 国产成人香蕉久久久久| 久久午夜福利电影| 久久人人爽人人爽人人爽| 一本久久a久久精品亚洲| 无码人妻久久一区二区三区| 99久久久精品| 久久国产成人精品国产成人亚洲| 久久精品中文字幕有码| 免费无码国产欧美久久18| 久久久久久久精品成人热色戒| 99久久99久久精品国产片果冻| 99精品久久久久中文字幕| 国产福利电影一区二区三区久久老子无码午夜伦不 | 欧美日韩中文字幕久久久不卡| 久久夜色精品国产www| 日韩人妻无码一区二区三区久久99| 久久婷婷五月综合成人D啪| 久久亚洲私人国产精品| 久久精品成人免费网站| 久久强奷乱码老熟女| 久久久久久亚洲Av无码精品专口| 色综合久久久久| 国产成人久久精品一区二区三区 | 激情伊人五月天久久综合| 国产—久久香蕉国产线看观看 | 国产成人无码精品久久久性色 | 久久精品国产网红主播|