網站首頁 編程語言 正文
今天我來補一下C語言篇的程序的編譯的一篇文章,也算是有一個結尾了。
程序的翻譯環境和執行環境
在ANSI C的任何一種實現中,存在兩個不同的環境 :
第1種是翻譯環境,在這個環境中源代碼被轉換為可執行的機器指令。
第2種是執行環境 ,它用于實際執行代碼。
一個.c的文件事如何變成.exe的可執行文件的呢?下面這張圖片是一個大概的過程:
編譯和鏈接
翻譯環境
- 組成一個程序的每個源文件通過編譯過程分別轉換成目標代碼( object code )。
- 每個目標文件由鏈接器( linker )捆綁在一 起,形成一個單一-而完整的可執行程序。
- 鏈接器同時也會引入標準C函數庫中任何被該程序所用到的函數,而且它可以搜索程序員個人的程序庫,將其需要的函數也鏈接到程序中。
編譯的幾個階段
接下來,我來用Linux平臺來給大家演示一下編譯的三個過程:
我們先編寫一個簡單C程序:
然后執行這樣一句指令:
gcc test.c
這句指令是讓gcc這個編譯器來編譯我們的代碼,執行完這句指令我們會發現會生成一個a.out這樣一個可執行文件,
我們執行再下面這樣一句指令:
./a.out
這樣我們就可以執行這個可執行文件了,
為了讓大家更好地感受到編譯的過程,我們來一步一步看:
預處理
我們執行再下面這樣一句指令,讓代碼預處理完之后就停下來:
gcc -E test.c -o test.i
這句指令的意思就是把預處理完之后的信息輸出到一個test.i的文件中。
可以發現的是,這里多了一個test,i的文件,我們可以打開看一看:
可以發現的是,有三個點發生了變化:
- 頭文件被展開
- 宏被文本替換了
- 注釋被刪除了
我們對原代碼做一個處理,不包含stdio.h的頭文件,我們自己寫一個頭文件:
再來看一下,預處理后的文件是什么樣子的:
效果通上面一樣。
所以預處理的幾個動作
- 頭文件的包含
- 預處理指令的完成(eg:#define、#pragma…)
- 注釋的刪除
編譯
執行再下面這樣一句指令讓文件進行編譯形成匯編代碼:
gcc -S test.c
執行完之后就可以生產出一個test.s的文件,我們可以打開看一看:
這里其實就是匯編代碼。
所以編譯的幾個動作
- 語法分析
- 詞法分析
- 語義分析
- 符號匯總
符號匯總: 符號匯總的都是全局的符號。例如上面我們的代碼頭文件就匯總了一個Add,.c文件就匯總的一個Add和main。
匯編
接下來我們執行這樣一條指令:
gcc -c test.c
對源文件進行匯編,結果生成了一個test.o的目標文件:
打開這個文件,我們會發現這是一個我們看不懂的二進制文件:
所以其實匯編是把匯編代碼轉換為二進制代碼(機器指令)。
這個過程還做了一件件事——形成符號表
鏈接
鏈接做的兩個事情
- 合并段表
- 符號表的合并和符號表的重定位
在Linux系統下,test.o二進制文件是用一個elf這樣的格式來組織文件的。
elf會把文件組織成一個段。test.o和Add.o都有一個段,那么我們怎樣才能看懂elf格式的文件呢?
我們有這樣一個工具叫做readelf,他可以看懂這樣一個文件,所以我們輸入這樣一條指令:
readelf test.o -a
我們就確實可以看到這樣一個段的存在。
然后這下面還有符號表的匯總:
其實a.out這個文件也是elf格式的,所以其實鏈接就是把這幾個elf格式的文件的段表合并,然后test中的Add函數就有了地址。
運行環境
程序執行的過程:
- 程序必須載入內存中。在有操作系統的環境中:一般這個由操作系統完成。在獨立的環境中,程序的載入必須由手工安排,也可能是通過可執行代碼置入只讀內存來完成。
- 程序的執行便開始。接著便調用main函數。
- 開始執行程序代碼。這個時候程序將使用一個運行時堆棧(stack),存儲函數的局部變量和返回地址。程序同時也可以使用靜態(static)內存,存儲于靜態內存中的變量在程序的整個執行過程一直保留他們的值。
- 終止程序。正常終止main函數;也有可能是意外終止。
原文鏈接:https://blog.csdn.net/weixin_58450087/article/details/121568793
相關推薦
- 2022-10-03 Objective-C優雅使用KVO觀察屬性值變化_IOS
- 2022-09-20 Python中類的mro與繼承關系詳解_python
- 2022-06-24 Python集合之set和frozenset的使用詳解_python
- 2022-11-25 Python中日期和時間的用法超強總結_python
- 2022-03-14 stream實現list根據對象中多個屬性分組,并取分組后最新數據
- 2022-11-02 Linux命令sed(流編輯器)的用法詳解_linux shell
- 2023-10-12 form組件的封裝(element ui ) 簡單版本
- 2022-11-09 golang包循環引用的幾種解決方案總結_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支