簡單來說,整個過程分為四個階段:預處理(Pre-Processing)、編譯(Compilation)、彙編(Assembling)、連結(Linking)。
注意:其中源程序、修改了的源程序和彙編程序都是文本文件,而可重定位目標程序和可執行目標程序都是二進位文件。
似乎到這裡就該結束了,因為整個過程已經講完了?。當然如果只是這樣介紹的話本文就沒有存在的價值了,我們還得再深入一點分別講解這四個階段?。
預處理首先我們準備一個簡單的Hello World程序,命名為main.c。
#include<stdio.h>
#defineinfo "Hello, world\n"intmain(){// A simple program.printf(info);return0;}
預處理階段做的事情就是預處理器(cpp)根據以字符#開頭的代碼修改原始的C程序。
比如#include <stdio.h>,將頭文件stdio.h中的內容加到程序文本中
比如#define info "Hello, world\n",會將宏定義的info替換成字符串「Hello, world\n」(當然我這裡只是為了舉例,一般我們不這麼寫)
此外,會將注釋比如// A simple program.刪除
預處理是直接對源文件進行處理(不關注語法規則), 然後得到另一個C程序,通常以.i作為文件擴展名。
我們可以在Linux系統(我這裡用的是Ubuntu)下直接使用gcc -E main.c -o main.i命令得到預處理後的C程序main.i。我截取了一部分(總共有800+行),可以很明顯的看到:引入了頭文件,info被替換了,注釋沒了,還添加了一些特殊的標記,告訴編譯器每行的來源,以便它可以使用它們來產生合理的錯誤消息。
#1"main.c"#1"<built-in>"#1"<command-line>"#31"<command-line>"#1"/usr/include/stdc-predef.h"134..externvoidfunlockfile(FILE*__stream)__attribute__((__nothrow__,__leaf__));#868"/usr/include/stdio.h"34#2"main.c"2#3"main.c"intmain(){printf("Hello, world\n");return0;}
編譯編譯階段做的事情就是編譯器(cc1)將C程序main.i翻譯成彙編語言程序main.s。
檢查C程序的語法錯誤
將文件翻譯成中間代碼,即彙編語言
可選地優化翻譯後的中間代碼,獲得更好的性能
我們可以使用gcc -S main.i -o main.s得到翻譯後的彙編程序main.s,截取部分如下。
.file"main.c".text.LC0:.string"Hello, world".text.globl mainmain:.LFB0:.cfi_startproc pushq%rbp movq%rsp,%rbp..LFE0:.size main,.-main.ident"GCC: (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0".section.note.GNU-stack,"",@progbits
其中的語句比如pushq %rbp描述了一條低級機器語言指令。彙編語言是有用的,它為不同高級語言的不同彙編器提供了通用的輸出語言,C彙編器和Fortran彙編器產生的輸出文件都是一樣的彙編語言。
彙編彙編階段做的事情就是彙編器(as)將main.s翻譯成機器語言指令,把這些指令打包成一種叫做可重定位目標程序的格式,並將結果保存在目標文件main.o(Windows下為xx.obj而Linux下為xx.o)中。main.o是一個二進位文件,如果我們使用文本編輯器打開main.o,會看到一堆亂碼。
我們可以使用gcc -c main.s -o main.o得到可重定位目標程序main.o,如下所示。
LF>?@@UH䈍=距ello,worldGCC:(Ubuntu7.3.0-27ubuntu1~18.04)7.3.0zRx#main.cmain_GLOBAL_OFFSET_TABLE_puts?ÿÿ C ??ÿÿ.symtab.strtab.shstrtab.rela.text.data.bss.rodata.comment.note.GNU-stack.rela.eh_frame @0&90d+BWR@@
不出所料的一堆亂碼?。
連結我們的main.c程序中使用了printf函數,printf函數是每個C編譯器都提供的標準C庫中的一個函數,它存在於一個名為printf.o的單獨預編譯好了的目標文件中。
連結階段做的事情就是連結器(ld)將需要用到的目標文件比如main.o和printf.o進行合併,並生成一個可執行(目標)文件,可以被加載到內存中,由系統執行。
實際上我們直接執行gcc main.c -o main命令得到的就是可執行文件,Windows下為.exe,Linux下默認為具有可執行權限的a.out,當然我們使用了-o來自定義輸出文件名。
以上就是整個編譯執行過程。
另外如果你想更好的提升你的編程能力,學好C語言C++編程!彎道超車,快人一步!筆者這裡或許可以幫到你~
UP在主頁上傳了一些學習C/C++編程的視頻教程,有興趣或者正在學習的小夥伴一定要去看一看哦!會對你有幫助的~
分享(源碼、項目實戰視頻、項目筆記,基礎入門教程)
歡迎轉行和學習編程的夥伴,利用更多的資料學習成長比自己琢磨更快哦!
免費學習書籍:
免費學習資料: