gcc a.c 究竟经历了什么
生活随笔
收集整理的這篇文章主要介紹了
gcc a.c 究竟经历了什么
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
link
你知道一次gcc命令究竟經歷了什么嗎?
我們先來看一段C語言示例源代碼:
// test.cc #include <stdio.h>int main() {printf("Hello 程序喵\n");return 0; } gcc test.cc./a.out Hello 程序喵
我們平時都會使用gcc來編譯程序,這一行簡單的命令其實經歷了很多復雜的過程:
- 預處理
- 編譯
- 匯編
- 鏈接
首先使用file看一下test.cc文件類型:
file test.cc test.c: C source, UTF-8 Unicode text我們接下來看看這每個過程都做了什么?
預處理
命令:
gcc -E test.cc -o test.i 或者 cpp test.cc -o test.i這里可以看出預處理后的文件和預處理前的文件類型是相同的,都是文本文件,也可以直接查看test.i的內容,里面代碼較多,就不貼上來了。
其實預處理主要操作有這幾個:
- 展開所有#define宏定義,進行文本替換
- 刪除程序中所有的注釋
- 處理所有的條件編譯,#if、#ifdef、#elif等
- 處理所有的#include指令,把這些頭文件的內容都復制到引用的源文件中
- 添加行號和文件名標識,方便編譯器產生警告及調試信息
- 保留所有的#pragma編譯器指令,因為編譯器會使用他們
編譯(生成匯編代碼)
命令:
gcc -S test.cc -o test.s再查看文件類型
file test.s test.s: assembler source, ASCII text .file "test.cc".text.section .rodata .LC0:.string "Hello \347\250\213\345\272\217\345\226\265".text.globl main.type main, @function main: .LFB0:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp.cfi_def_cfa_register 6leaq .LC0(%rip), %rdicall puts@PLTmovl $0, %eaxpopq %rbp.cfi_def_cfa 7, 8ret.cfi_endproc .LFE0:.size main, .-main.ident "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0".section .note.GNU-stack,"",@progbits如圖二,編譯過程就是把預處理后的文件進行一系列操作生成相應的匯編文件:
- 詞法分析:又稱詞法掃描,通過掃描器,利用有限狀態機的算法將源碼中的字符串分割成一系列記號,如加減乘除數字括號等。
- 語法分析:使用語法分析器對詞法分析產生的記號運用上下文無關語法的手段進行語法分析,產生語法分析樹。這期間如果表達式不合法(括號不匹配等),就會報錯。
- 語義分析:語法分析檢查表達式是否合法,語義分析檢查表達式是否有意義,如浮點型整數賦值給指針,編譯器就會報錯。
- 中間語言生成:做一些語法樹優化,如6+2=8。
- 目標代碼生成及優化:將中間代碼生成目標匯編代碼。
匯編
命令:
gcc -c test.s -o test.o 或 as test.s -o test.o 7f45 4c46 0201 0100 0000 0000 0000 0000 0100 3e00 0100 0000 0000 0000 0000 0000 0000 0000 0000 0000 d002 0000 0000 0000 ... ... ... 0000 0000 0000 0000 6802 0000 0000 0000 6100 0000 0000 0000 0000 0000 0000 0000 0100 0000 0000 0000 0000 0000 0000 0000查看文件類型:
file test.o testt.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped使用匯編器將匯編代碼轉成機器可以執行的指令,其實就是將匯編指令和機器指令按照對照表一一翻譯。
鏈接
為什么匯編器不直接生成可執行文件而是生成一個目標文件呢,因為一個文件需要依賴其它好多個庫,這些庫的符號需要通過鏈接過程才可以互相配合生成一個可執行文件,需要經歷地址和空間分配、符號決議、重定位等步驟,這塊內容較多,后續會詳細介紹,現在我們可以簡單的通過ldd查看一下可執行程序需要依賴的庫,這些庫都需要在鏈接過程中被鏈接才可以使用。
ldd a.out linux-vdso.so.1 (0x00007ffe59c5b000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efcd6269000)/lib64/ld-linux-x86-64.so.2 (0x00007efcd685c000)總結
以上是生活随笔為你收集整理的gcc a.c 究竟经历了什么的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++20四大之一:module特性详解
- 下一篇: 手把手教你构建 C 语言编译器