• <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

            統計

            • 隨筆 - 85
            • 文章 - 0
            • 評論 - 17
            • 引用 - 0

            常用鏈接

            留言簿(6)

            隨筆分類

            隨筆檔案

            收藏夾

            全是知識啊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

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

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

            1、預處理

                開篇簡述:預處理是C語言程序從源代碼變成可執行程序的第一步,主要是C語言編譯器對各種預處理命令進行處理,包括頭文件的包含、宏定義的擴展、條件編譯的選擇等。

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

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

          1. 打印出預處理之后的結果:gcc -E hello.c

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

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

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

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

                如果編譯時加上-DDEBUG選項,那么編譯器就會把printf所在的行編譯進目標代碼,從而方便地跟蹤該位置的某些程序狀態。這樣-DDEBUG就可以當作一個調試開關,編譯時加上它就可以用來打印調試信息,發布時則可以通過去掉該編譯選項把調試信息去掉。

            本節參考資料:
            [1] C語言教程第九章:預處理
            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、編譯(翻譯)

                開篇簡要:編譯之前,C語言編譯器會進行詞法分析、語法分析(-fsyntax-only),接著會把源代碼翻譯成中間語言,即匯編語言。如果想看到這個 中間結果,可以用-S選項。需要提到的是,諸如shell等解釋語言也會經歷一個詞法分析和語法分析的階段,不過之后并不會進行“翻譯”,而是“解釋”, 邊解釋邊執行。

                把源代碼翻譯成匯編語言,實際上是編譯的整個過程中的第一個階段,之后的階段和匯編語言的開發過程沒有什么區別。這個階段涉及到對源代碼的詞法分析、語法檢查(通過-std指定遵循哪個標準),并根據優化(-O)要求進行翻譯成匯編語言的動作。

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

            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    #默認情況下,gcc是允許在程序中間聲明變量的,但是turboc就不支持
            hello.c: In function ‘main’:
            hello.c:5: error: ISO C90 forbids mixed declarations and code



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

                語法檢查之后就是翻譯動作,gcc提供了一個優化選項-O,以便根據不同的運行平臺和用戶要求產生經過優化的匯編代碼。例如,

            Quote:

            $ gcc -o hello hello.c            #采用默認選項,不優化
            $ gcc -O2 -o hello2 hello.c        #優化等次是2
            $ gcc -Os -o hellos hello.c        #優化目標代碼的大小
            $ 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                #可能是代碼比較少的緣故,執行效率看上去不是很明顯
            hello, world

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

            $ time ./hellos                #雖然目標代碼小了,但是執行效率慢了些
            hello, world

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



                根據上面的簡單演示,可以看出gcc有很多不同的優化選項,主要看用戶的需求了,目標代碼的大小和效率之間貌似存在一個“糾纏”,需要開發人員自己權衡。

                下面我們通過-S選項來看看編譯出來的中間結果,匯編語言,還是以之前那個hello.c為例。
            Quote:

            $ gcc -S hello.c        #默認輸出是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



                不知道看出來沒?和我們在課堂里學的intel的匯編語法不太一樣,這里用的是AT&T語法格式。如果之前沒接觸過AT&T的,可以看看 參考資料[2]。如果想學習Linux下的匯編語言開發,從下一節開始哦,下一節開始的所有章節基本上覆蓋了Linux下匯編語言開發的一般過程,不過這 里不介紹匯編語言語法。

                這里需要補充的是,在寫C語言代碼時,如果能夠對編譯器比較熟悉(工作原理和一些細節)的話,可能會很有幫助。包括這里的優化選項(有些優化選項可能在匯 編時采用)和可能的優化措施,例如字節對齊(可以看看這本書"Linux_Assembly_Language_Programming"的第六小節)、 條件分支語句裁減(刪除一些明顯分支)等。

            本節參考資料

            [1] Guide to Assembly Language Programming in Linux(pdf教程,社區有下載)
            http://oss.lzu.edu.cn/modules/wfdownloads/singlefile.php?cid=5&lid=94
            [2] Linux匯編語言開發指南(在線):
            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 體系結構的匯編語言
            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 的內聯匯編
            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 隨意門 閱讀(905) 評論(0)  編輯 收藏 引用

            亚洲伊人久久大香线蕉苏妲己| 久久综合鬼色88久久精品综合自在自线噜噜 | 久久人妻AV中文字幕| 久久综合亚洲欧美成人| 青青草国产精品久久久久| 久久天天躁狠狠躁夜夜2020一| 天天躁日日躁狠狠久久| 久久无码AV中文出轨人妻| 人人狠狠综合久久88成人| 久久精品中文字幕久久| 色播久久人人爽人人爽人人片aV| 久久亚洲国产午夜精品理论片| 国内精品久久久久久中文字幕 | 久久亚洲精品无码播放| 久久99久久99精品免视看动漫| 91精品国产高清91久久久久久| 色偷偷88888欧美精品久久久| 成人精品一区二区久久| 亚洲国产成人久久综合一| 99久久精品免费看国产一区二区三区 | 97香蕉久久夜色精品国产| 香蕉久久影院| 亚洲国产成人久久综合一| 久久国产精品77777| 好久久免费视频高清| 91精品国产乱码久久久久久| 伊人久久大香线蕉精品不卡 | 国产精品99久久久久久猫咪| 香蕉久久夜色精品升级完成| 久久天天躁狠狠躁夜夜av浪潮| 国产精品99久久久久久www| 97久久综合精品久久久综合 | 中文精品久久久久国产网址| 久久青青草原亚洲av无码app| 亚洲国产天堂久久综合| 亚洲国产精品无码久久久久久曰 | 99国产欧美精品久久久蜜芽| 午夜人妻久久久久久久久| 无码日韩人妻精品久久蜜桃| 人妻丰满AV无码久久不卡| 亚洲精品无码久久久久去q|