1. 程式人生 > >C/C++程式的編譯連結過程

C/C++程式的編譯連結過程

在以前學習C語言的時候,想必大家寫的第一個程式碼都是“hello world”吧。在以前我們除錯一個程式碼的時候是在vc++6.0或者是在vs上面除錯的,這種就是整合開發環境,它為我們簡化了一個程式碼的編譯連結的過程但是卻對初學者又蒙上了面紗。而當我們學習了更多一些知識的時候就該看看這個面紗下面到底隱藏著什麼。
大家都知道我們所編寫的C語言程式只是一個字尾為“.c”的檔案,這個檔案是不能直接被計算機所執行的,我們要將這高階語言轉化成計算機所能執行的二進位制語言。而把這個普通檔案進過處理轉換成計算機處理的可執行檔案的過程就是編譯連結。而其實這個過程要細分的話要分解4個步驟:預處理、編譯、彙編、連結。

而用GCC來編譯這個“hello world”程式的過程為下圖:
這裡寫圖片描述
而編譯的流程圖為:
這裡寫圖片描述

一、預處理(Preproceessing)
預處理是根據預處理指令組裝新的C、C++程式,進過預處理會產生一個沒有巨集定義、沒有條件編譯指令、沒有特殊符號輸出的檔案。在linux下命令為:gcc -E hello.c -o hello .i 或者 cpp hello .c > hello.i
預編譯條件下的處理規則如下:
1、將所有的“#define”刪除,並且展開所有的巨集定義。
2、處理所有的條件編譯指令。
比如“#if”“#ifdef”“#elif”“#else”“#endif”。
3、處理“#include”預編譯指令,將被包含的檔案插入到該預編譯指令的位置。這個過程是遞迴進行的,也就是說被包含的檔案可能還包含其他檔案。
4、刪除所有的註釋“//”和“/* */”。
5、新增行號和檔案標識,比如#2“hello.c”2,以便於編譯時編譯器產生除錯用的行號資訊及用於編譯時產生編譯錯誤或警告時能夠顯示行號。
6、保留所有的#pragma編譯器指令,編譯器要使用它們。

二、編譯(Compilation)
編譯是對於預處理完的檔案進行一些列的詞法分析、語法分析、語義分析及優化後產生相應的彙編程式碼檔案。gcc -S hello.i -o hello.s
或者 gcc -S hello.c -o hello.s
注:現代版本的GCC把預處理和編譯兩個步驟合成一個步驟,用cc1工具來完成,gcc就是對於一些後臺程式的封裝,比如:預編譯編譯程式cc1、彙編器as、連結器Id。
編譯後的彙編程式碼(hello.s)如下:

   .file   "hello.c"
    .section    .rodata
.LC0:
    .string
"Hello, world." .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $16, %esp movl $.LC0, (%esp) call puts movl $0, %eax leave ret .size main, .-main .ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3" .section .note.GNU-stack,"",@progbits


編譯時的技巧:
編譯的作用是對源程式進行詞法檢查、語法檢查和中間程式碼生成。編譯時對檔案中的全部內容進行檢查,如果有語法錯誤,編譯結束後會顯示出所有的編譯出錯資訊,開發人員可以根據錯誤提示修改程式。對於新寫的一個保護多個檔案的工程,一開始採用原始檔分別編譯,這樣容易發現每個原始檔的自身錯誤,限定了錯誤的範圍,如果一開始就採用全部編譯,多個原始檔可能會產生許多錯誤,無形中增加了開發難度。如果每個原始檔都通過了編譯,再將所有檔案進行編譯。對原始檔分別編譯對於除錯,糾錯是一種很好的方法。

三、彙編(Assembly)
彙編器是將彙編程式碼轉化成機器可以執行的命令,每一條彙編語句都對應一條機器指令,並生成可重定位目標程式的.o檔案,該檔案為二進位制檔案,位元組編碼時機器指令。彙編相對於編譯的過程比較簡單,根據彙編指令表和機器指令表一一進行翻譯就可以了。所以彙編器的彙編過程相對與編譯器是比較簡單的,命令如下:
as hello.s -o hello.o
或者
gcc -c hello.s -o hello.o

四、連結(Linking)
連結分為:靜態連結、動態連結。
靜態連結:在編譯階段就把靜態庫就加到可執行檔案中去,這樣可執行檔案就會比較大。
動態連結:在連結階段僅僅只加入一些描述資訊,而程式執行時再從系統中把相應動態庫載入到記憶體中去。
連結時通過呼叫聯結器來連結程式執行需要的一大堆目標檔案,以及所依賴的其他庫的檔案,最後生成可執行檔案。連結程式的主要工作就是將有關的目標檔案彼此相連線,也就是將在一個檔案中引用的符號同該符號在另外一個檔案中的定義連線起來,使得所有的這些目標檔案成為一個能夠被作業系統裝入執行的統一整體。
靜態連結的過程:
這裡寫圖片描述
“hello world”程式的編譯連結過程就是這樣的,那麼編譯器和聯結器到底是做了什麼呢?
其實我們回到編譯器本身的職責上來看,編譯的過程一般是分為6個步驟:掃描、語法分析、語義分析、原始碼優化、程式碼生成、目的碼優化。
如圖所示:
這裡寫圖片描述
掃描:掃描也就是詞法分析。掃描器把原始碼的字元序列分割成一系列的記號,有一個叫lex的程式可以實現詞法掃描,可以按照使用者描述好的詞法規則將輸入的字串分割成一個個記號。
語法分析:語法分析器產生語法樹,yacc工具實現語法分析。
語義分析:靜態語義(編譯器可以確定的語義)、動態語義(執行期才可以確定的語義)。
原始碼優化:原始碼優化器將整個語法樹轉化為中間程式碼(與目標機器和環境變數無關),中間程式碼使編譯器分為前端和後端,編譯器前端負責產生無關的中間程式碼,編譯器後端將中間程式碼轉化為目標機器程式碼。
目的碼生成:程式碼生成器。
目的碼優化:目的碼優化器。

擴充套件:

extern:這是告訴編譯器,這個符號在別的編譯單元裡定義,也就是要把這個符號放到未解決符號表裡去。(外部連結)

外部連結的利弊:外部連結的符號,可以在整個程式範圍內使用(因為匯出了符號),但是同時要求其他的編譯單元不能匯出相同的符號。

內部連結的利弊:內部連結的符號,不能在別的編譯單元內使用。但是不同的編譯單元可以擁有同樣名稱的內部連結符號。

為什麼標頭檔案只可以有宣告不能有定義?
標頭檔案可以被多個編譯單元包含,如果標頭檔案裡有定義,那麼每個包含這個標頭檔案的編譯單元就都會對同一個符號進行定義,如果該符號為外部連結,則會導致duplicated external simbols。因此如果標頭檔案裡要定義,必須保證定義的符號只能具有內部連結。

為什麼常量預設為內部連結而變數不是?
這就是為了能夠在標頭檔案裡如const int n = 0這樣的定義常量。由於常量是隻讀的,因此即使每個編譯單元都擁有一份定義也沒有關係。如果一個定義於標頭檔案裡的變數擁有內部連結,那麼如果出現多個編譯單元都定義該變數,則其中一個編譯單元對該變數進行修改,不會影響其他單元的同一變數,會產生意想不到的後果。

為什麼函式預設是外部連結?
雖然函式是隻讀的,但是和變數不同,函式在程式碼編寫的時候非常容易變化,如果函式預設具有內部連結,則人們會傾向於把函式定義在標頭檔案裡,那麼一旦函式被修改,所有包含了該標頭檔案的編譯單元都要被重新編譯。另外,函式裡定義的靜態區域性變數也將被定義在標頭檔案裡。

為什麼公共使用的行內函數要定義在標頭檔案裡?
因為編譯時編譯單元之間互相不知道,如果行內函數被定義於.cpp檔案中,編譯其他使用該函式的編譯單元的時候沒有辦法找到函式的定義,因此無法對函式進行展開。所以說如果行內函數定義於.cpp檔案裡,那麼就只有這個cpp檔案可以是用這個函式。

相關推薦

C語言的編譯連結過程的介紹

C語言的編譯連結過程要把我們編寫的一個c程式(原始碼)轉換成可以在硬體上執行的程式(可執行程式碼),需要進行編譯和連結。編譯就是把文字形式原始碼翻譯為機器語言形式的目標檔案的過程。連結是把目標檔案、作業系統的啟動程式碼和用到的庫檔案進行組織形成最終生成可執行程式碼的過程。

程序虛擬地址空間 程式編譯連結過程

1..程式編譯、連結、執行的過程。 (1)預編譯階段**** 生成*.i檔案。 處理原始碼檔案,即以“#”開始的預編譯指令。 有展開巨集;去註釋;新增行號;保留所有#pragma編譯指令。

C/C++程式編譯連結過程

在以前學習C語言的時候,想必大家寫的第一個程式碼都是“hello world”吧。在以前我們除錯一個程式碼的時候是在vc++6.0或者是在vs上面除錯的,這種就是整合開發環境,它為我們簡化了一個程式碼的

C程式編譯連結過程

程式由原始檔編譯得到可執行檔案看起來好像是很簡單的過程,windows的IDE環境下,點一下bulid就可以生成可執行檔案,在Linux環境下,gcc編譯器也提供了很多選項可以很方便的從原始檔生成可執行檔案。事實上程式的編譯和連結是一個非常複雜的過程,IDE幫我

C程式編譯連結】gcc使用命令介紹 gcc的使用簡介與命令列引數說明

1.gcc或者g++安裝rpm -qa|grep gcc ==>檢查gcc是否安裝gcc -v ==>檢查gcc版本 編譯器會在可執行檔案中植入一些資訊,可執行檔案會變大。一般開發時候使用 -g ,編譯一個 “release 版本” 時不使用 -g 編譯。gcc如果是最新的則不重

C程式編譯連結】gcc使用命令介紹 GCC編譯器編譯連結  

1.gcc安裝 rpm -qa|grep gcc ==>檢查gcc是否安裝 gcc -v ==>檢查gcc版本 yum -y install gcc ==>安裝gcc  2.基本語法 gcc最基本的用法是:gcc [options]

C&C++編譯連結過程

本文講解編譯連結過程,因為才疏學淺可能有些不準確。 使用c檔案的編譯連結過程,使用Linux系統用來檢視檔案資訊。 (1)從原始檔main.c編譯連結成main.exe,需要經歷如下步驟:     (2)其中符號和符號表是什麼呢?段又是什麼? 段:在一個程

c語言編譯連結過程

學過C語言的人都應該知道,我們所編輯的C語言程式是不能直接放到機器上執行的,它只不過是一個帶”.c”字尾的檔案(也稱為原始碼)而已,需要經過一定的處理才能轉換成機器上可執行的可執行檔案。我們將對C語言的這種處理過程稱為編譯與連結。 編譯就是把文字形式原始碼翻譯

C語言】編譯連結的詳細過程

相信大家在學習C語言時,肯定會有這麼一個問題,計算機把一段程式轉化為能識別的二進位制機器語言的過程是什麼?        大體的來講需要經歷四個過程:預處理,編譯,彙編,連結。接下來我會給大家一一說明。 首先預處理過程所做的操作是 進行程式的巨集替換,去註釋,標頭檔案按照路徑展開,以及條件編譯。 接著

LInux下C語言原始碼編譯過程

原始碼編譯的過程:原始碼–>預處理–>編譯–>彙編–>連結–>執行 大致可分為三步:./configure—>make—>make install 總的來說:make就做了編譯彙編的工作,他是根據makefil

make,makefile和程式編譯連結過程

一,Linux下程式執行過程 1,在一個目錄下新建三個檔案:main.c hello.c hello.h分別編寫他們如下圖: 2,想要讓這個程式執行起來,就必須對上面的三個檔案分別進行編譯連結執行,如下圖: 通過上面這個過程。我們可

C語言之編譯連結全過程分析

C語言的編譯連結過程要把我們編寫的一個c程式(原始碼)轉換成可以在硬體上執行的程式(可執行程式碼),需要進行編譯和連結。編譯就是把文字形式原始碼翻譯為機器語言形式的目標檔案的過程。連結是把目標檔案、作業系統的啟動程式碼和用到的庫檔案進行組織,形成最終生成可執行程式碼的過程。

程式編譯連結過程

原文連結: https://www.cnblogs.com/kekec/p/3238741.html還是從HelloWorld開始說吧...#include <stdio.h> int main(int argc, char* argv[]) { pri

GCC 顯示程式詳細編譯-連結過程

   很多時候,我們對於程式的編譯和連結過程很少關注。特別是使用強大的IDE之後,更多人只是會使用IDE進行程式設計。這樣,一旦出現編譯或者連結問題,有時候會很難找到編譯或者連結問題。理解編譯和連結的過程,對於編寫程式以及除錯問題都有很大的幫助。    事實上,gcc提供了

c語言複習之連結過程簡介

本文是基於唐佐林老師的課程的總結工程中的每個c語言原始檔被編譯後生產目標檔案,這些目標檔案如何生成最終的可執行檔案?聯結器的主要作用是把各個模組之間相互引用的部分處理好,使得各個模組之間能夠正確的銜接                                     

Linux系統使用入門進階總結(6)——Ubuntu下gcc/g++編譯連結過程

文章轉自: https://blog.csdn.net/VennyJin/article/details/82794331 這裡講的是最簡單的c/c++檔案在linux下編譯連結的過程,後期還可以使用cmake來完成更復雜的工程構建過程。請關注博主的後續文章哈~~~ Ubuntu下gcc

ARM Linux編譯連結過程分析

cmd_vmlinux := arm-iwmmxt-linux-gnueabi-ld -EL-p --no-undefined -X -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o arch/arm/kernel/init_ta

程式編譯連結

預處理:.c --> .i $ gcc -E hello.c -o hello.i 編譯: .i 或 .c --> .s $ gcc -S hello.i -o hello.s 彙編:.s --> .o $ gcc -c hello.s -o hello

程式連結過程簡介

程式的連結過程:一 聯結器的基本功能1 對各個目標模組中沒有定義的變數,在其它目標檔案中找到相關的定義2 把不同目標檔案中生成的相同型別的段進行合併3 把不同目標檔案中的變數進行地質重定位二 可執行檔案的裝載動態連結庫:程式在執行的時候才去定位這個庫,並且把這個庫連結到程序的

ios程式編譯連結引數 all_load 的 ld duplicate symbol 的bug及修復

duplicate symbol _OBJC_CLASS 的幾種原因 1.專案檔案裡面有多個相同名字的檔案; 2.import 的時候.h寫成.m了;xcode的自動完成特性,在你匯入標頭檔案的時候,將".h"補成了".m" 3.有多個main檔案。 問題 -all_lo