日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

从编写c语言源程序到运行,c语言 源代码到可执行程序的过程

發(fā)布時間:2023/12/9 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从编写c语言源程序到运行,c语言 源代码到可执行程序的过程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

從源代碼(.c)到可執(zhí)行文件一共分為五個步驟:

1、編譯預(yù)處理

2、編譯階段

3、優(yōu)化階段

4、匯編過程

5、鏈接程序

一、編譯預(yù)處理(.i) ------>"替代"成一個沒有宏定義、沒有條件編譯指令、沒有特殊符號的輸出文件 。

讀取c源程序,對其中的偽指令(以#開頭的指令)和特殊符號進行處理。偽指令主要包括以下四個方面:

(1)宏定義指令。

如:#define M 9 、#define SQUARE(x) ((x)*(x))?、#undef(取消宏定義),宏一般都是大寫(以區(qū)別函數(shù))。注意:作為字符串的常量則不被替換。

#define?M?9

int?main()

{

a=M;

printf("M=%d",a);

//printf("M=%d",9);

}

#define?SQUARE(x)?((x)*(x))

//?x?*?x

int?main()

{

int?a=4;

printf("%d\n",SQURE(a+1));

//printf("%d\n",((4+1)*(4+1)));

//printf("%d\n",??4+1?*?4+1??);

}

注意:參數(shù)列表的左括號必須與SQUARE緊鄰、不能吝嗇括號,否則會出現(xiàn)計算邏輯的問題。

(2)條件編譯指令。

如:#ifdef,#ifndef,#else,#elif,#endif,等等。這些偽指令使得程序員可以通過定義不同的宏來決定編譯程序?qū)δ男┐a進行處理(過濾作用),可避免重復(fù)復(fù)制代碼。

#ifdef 標識符

程序段 1

#else

程序段 2

#endif

它的功能是,如果標識符已被 #define 命令定義過則對程序段 1 進行編譯;否則對程序段 2 進行編譯。

(3)頭文件包含指令。

如:#include 、#include "stdio.h"等等。#include 是將已存在文件的內(nèi)容復(fù)制到當前文件中,它可以使某些定義可以供多個不同的C源程序使用,只需加上一條#include語句 ,預(yù)編譯就會自己把頭文件中的定義統(tǒng)統(tǒng)都加入到它所產(chǎn)生的輸出文件中,以供編譯,而不用程序員再行定義。

注意: <> 和 "" 的區(qū)別。 <> 表示預(yù)處理到系統(tǒng)規(guī)定的路徑中去獲得這個文件,而 "" 則表示預(yù)處理應(yīng)在當前目錄中查找文件名為?stdio.h 的文件,若沒有找到,則按系統(tǒng)指定的路徑信息,搜索其他目錄。

(4)特殊符號。

比如一些預(yù)定義符號__FILE__(表示正在編譯的文件的名字)、 __TIME__(表示編譯時刻的時間字符串)、 __LINE__(表示正在編譯的文件的行號)、 __DATE__(表示編譯時刻的日期字符串)、 __STDC__(判斷該文件是不是定義成標準 C 程序),預(yù)編譯程序?qū)τ谠谠闯绦蛑谐霈F(xiàn)的這些串將用合適的值進行替換。

printf?("file:%s\tline:%d\tdate:%s\ttime:%s\n",__FILE__???,__LINE__,?__DATE__,__TIME__?);

# 運算符 和 ## 預(yù)算符

a、如果我們希望在字符串中包含宏參數(shù),那我們就可以使用“#”,它可以把語言符號轉(zhuǎn)化為字符串。

#include

#define?SQR(x)?printf("The?square?of?"#x"?is?%d.\n",((x)*(x)))

int?main()

{

SQR(8);

}

運行結(jié)果是:The square of 8 is 64.

b、“##”就是個粘合劑,可以將前后兩部分粘合起來。

#include

#define?XNAME(n)?x?##?n

int?main()

{

XNAME(8)?;

}

運行結(jié)果是:x8

二、編譯階段(.s) ------->?翻譯成等價的中間代碼表示或匯編代碼

經(jīng)過預(yù)編譯得到的輸出文件中,將只有常量。

如:數(shù)字、字符串、變量的定義,以及C語言的關(guān)鍵字(main,if,else,for,while,{,},+,-,*,\)等等。

預(yù)編譯程序,所要作的工作就是通過詞法分析、語義分析、符號匯總和語法分析,在確認所有的指令都符合語法規(guī)則之后,將其翻譯成等價的中間代碼表示或匯編代碼。

三、優(yōu)化階段(執(zhí)行的效率的提高)。

(1)對中間代碼的優(yōu)化。

(2)與機器的硬件結(jié)構(gòu)有關(guān),比如:利用機器的各個硬件寄存器存放的有關(guān)變量的值,減少對于內(nèi)存的訪問次數(shù)等等。

四、匯編過程(.o) ------> 生成相應(yīng)的目標文件 。

匯編過程對匯編器比較簡單,匯編器只需將匯編代碼轉(zhuǎn)變成機器可以執(zhí)行的指令,每一個匯編都有一條對應(yīng)的機器指令,它沒有復(fù)雜的語法也沒有語義,也不需要指令優(yōu)化,僅僅對照機器指令和匯編指令的對照表一一翻譯。

目標文件中所存放的是與源程序等效的目標的機器語言代碼。

目標文件由段組成。一個目標文件中至少有兩個段:

代碼段  主要包含的是程序的指令。該段一般是可讀、可執(zhí)行、不可寫。

數(shù)據(jù)段  主要存放程序中的全局變量或靜態(tài)的數(shù)據(jù)。一般數(shù)據(jù)段都是可讀、可執(zhí)行、可寫 。

五、鏈接程序 ------->將有關(guān)的目標文件彼此相連接 。

由匯編程序生成的目標文件并不能立即就被執(zhí)行,其中可能還有許多沒有解決的問題,比如函數(shù)調(diào)用、模塊間的變量訪問。

鏈接包括:地址和空間分配、重定位、符號決議。

重定位:假如有一個全局變量叫var,在目標文件 A 里,而目標文件 B 里邊要使用var變量,我們編譯目標文件 B ,由于在編譯的時候編譯器找不到var的地址,編譯器在無法確定地址的情況下就將地址置為0,當鏈接器將 A 和 B 鏈接后,變量var的地址會確定下來,鏈接器將會修改地址,這地址修改的過程就稱作重定位。

鏈接處理分為兩種:

(1)靜態(tài)鏈接,就是函數(shù)的代碼將從靜態(tài)鏈接庫中被拷貝到可執(zhí)行程序中(靜態(tài)鏈接庫實際上是一個目標文件的集合,其中的每個文件含有庫中的一個或者一組相關(guān)函數(shù)的代碼),但這樣使得可執(zhí)行程序體積變大。

(2)動態(tài)鏈接,就是需要鏈接的代碼放到一個共享對象中,鏈接程序只用記錄可執(zhí)行程序?qū)硇枰玫拇a信息,在可執(zhí)行文件被執(zhí)行時,動態(tài)鏈接庫的全部內(nèi)容將被映射到運行時相應(yīng)進程的虛地址空間, 動態(tài)鏈接程序?qū)⒏鶕?jù)可執(zhí)行程序中記錄的信息找到相應(yīng)的函數(shù)代碼。 比靜態(tài)鏈接節(jié)約內(nèi)存。

經(jīng)過上述五個過程,C源程序就最終被轉(zhuǎn)換成可執(zhí)行文件了。

總結(jié)

以上是生活随笔為你收集整理的从编写c语言源程序到运行,c语言 源代码到可执行程序的过程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。