柚子快報(bào)邀請(qǐng)碼778899分享:c語(yǔ)言 C--編譯和鏈接見解
柚子快報(bào)邀請(qǐng)碼778899分享:c語(yǔ)言 C--編譯和鏈接見解
歡迎各位看官!如果您覺得這篇文章對(duì)您有幫助的話 歡迎您分享給更多人哦 感謝大家的點(diǎn)贊收藏評(píng)論 感謝各位看官的支持!?。?/p>
一:翻譯環(huán)境和運(yùn)行環(huán)境
在ANSIIC的任何一種實(shí)現(xiàn)中,存在兩個(gè)不同的環(huán)境
1,翻譯環(huán)境:源代碼 被轉(zhuǎn)換成 可執(zhí)行的機(jī)器指令(二進(jìn)制指令) (電腦只能讀懂這個(gè))
2,執(zhí)行環(huán)境:實(shí)際執(zhí)行代碼
其實(shí)翻譯環(huán)境是由編譯和鏈接兩個(gè)?的過(guò)程組成的,?編譯?可以分解成:預(yù)處理(有些書也叫預(yù)編
譯)、編譯、匯編三個(gè)過(guò)程
二.預(yù)處理(預(yù)編譯),編譯和匯編
2.1:預(yù)處理(預(yù)編譯)
在預(yù)處理階段。**源文件和頭文件**都會(huì)被處理成后綴為(.i)的文件
```c
命令:gcc -E test.c -o test.i **(-E到預(yù)處理結(jié)束,-o,生成test.i文件)**
> 1.預(yù)處理階段主要處理那些源?件中#開始的預(yù)編譯指令。
?如:#include,#define,處理的規(guī)則如下:
> ? 將所有的 #define 刪除,并展開所有的宏定義。**(將#define定義的內(nèi)容展開)
> ? 處理所有的條件編譯指令,如: #if、#ifdef、#elif、#else、#endif 。
> ? 處理#include 預(yù)編譯指令,將包含的頭?件的內(nèi)容插?到該預(yù)編譯指令的位置。
這個(gè)過(guò)程是遞歸進(jìn)行的,也就是說(shuō)被包含的頭?件也可能包含其他?件。**
> ? 刪除所有的注釋(變成空格) ? 添加行號(hào)和文件名標(biāo)識(shí),方便后續(xù)編譯器生成調(diào)試信息等。
> ? 或保留所有的#pragma的編譯器指令,編譯器后續(xù)會(huì)使用。
經(jīng)過(guò)預(yù)處理后的 .i 文件中不再包含宏定義,因?yàn)楹暌呀?jīng)被展開。并且包含的頭文件呢都被插入到 .i文件中。所以當(dāng)我們無(wú)法知道宏定義或者頭文件是否包含正確的時(shí)候,可以查看預(yù)處理后的 .i 文件來(lái)確認(rèn)。
2.2編譯
詞法分析,語(yǔ)法分析,語(yǔ)義分析。將C語(yǔ)言代碼轉(zhuǎn)換成匯編代碼
gcc -S test.i -o test.s //生成test.s
例如: arr[index]=(index+4)*(2+6)
2.3 匯編
匯編器是將匯編代碼轉(zhuǎn)轉(zhuǎn)變成機(jī)器可執(zhí)行的指令(二進(jìn)制),每?個(gè)匯編語(yǔ)句?乎都對(duì)應(yīng)?條機(jī)器指令。就是根據(jù)匯編指令和機(jī)器指令的對(duì)照表??的進(jìn)?翻譯,也不做指令優(yōu)化。匯編的命令如下:
gcc -c test.s -o test.o
對(duì)test.c處理成test.o(二進(jìn)制文件)
鏈接是?個(gè)復(fù)雜的過(guò)程,鏈接的時(shí)候需要把?堆?件鏈接在?起才?成可執(zhí)行程序。 鏈接過(guò)程主要包括:地址和空間分配,符號(hào)決議和重定位等這些步驟。 鏈接解決的是?個(gè)項(xiàng)?中多?件、多模塊之間互相調(diào)用的問(wèn)題
三.運(yùn)行環(huán)境
程序必須載入內(nèi)存中。在有操作系統(tǒng)的環(huán)境中:?般這個(gè)由操作系統(tǒng)完成。在獨(dú)立的環(huán)境中(單片機(jī),里面無(wú)操作系統(tǒng)),程序的載入必須手工安排,也可能是通過(guò)可執(zhí)行代碼置入只讀內(nèi)存來(lái)完成。 3. 程序的執(zhí)行便開始。接著便調(diào)用main函數(shù)。 4. 開始執(zhí)?程序代碼。這個(gè)時(shí)候程序?qū)⑹??個(gè)運(yùn)?時(shí)堆棧(函數(shù)棧幀)(stack),存儲(chǔ)函數(shù)的局部變量和返回地址 (程序是由一個(gè)個(gè)函數(shù)組成的,每創(chuàng)建一個(gè)函數(shù)就會(huì)創(chuàng)建一個(gè)運(yùn)行時(shí)堆棧,運(yùn)行時(shí)維護(hù),結(jié)束時(shí)銷毀)程序同時(shí)也可以使?靜態(tài)(static)內(nèi)存,存儲(chǔ)于靜態(tài)內(nèi)存中的變量在程序的整個(gè)執(zhí)行過(guò)程?直保留他們的值。 5. 終止程序。正常終止main函數(shù);也有可能是意外終止。
四.預(yù)處理詳解
1.1define 定義常量
__FILE__ //進(jìn)?編譯的源?件
__LINE__ //?件當(dāng)前的?號(hào)
__DATE__ //?件被編譯的?期
__TIME__ //?件被編譯的時(shí)間
__STDC__ //如果編譯器遵循ANSI C(C語(yǔ)言標(biāo)準(zhǔn)),其值為1,否則未定義//vs并未完全遵守
C語(yǔ)言設(shè)置的預(yù)定義符號(hào),可以直接使用,預(yù)定義符號(hào)也是在預(yù)處理期間處理的
并且預(yù)處理后代碼中**預(yù)定義符號(hào)**(你猜為什么叫預(yù)定義符號(hào))就被替換了
printf("%s\n",C:\code\c-language-411\test_9_5\test_9_5\test.c)以下類推
63
Sep 5 2024
19:58:36
#include
#define M 100
#define STR "hehe"
#define reg register
int main()
{
int arr[M] = { 0 };
int a = M;
printf("%d\n", M); 100 大家都是預(yù)處理的時(shí)候就已經(jīng)換到printf("%d\n",100)這種了
printf(STR); hehe
reg int b= 10;//預(yù)處理后是register int b=10
return 0;
}
1.2:關(guān)于define的一些規(guī)則
#define不要加;
#define M 100;
if(a)
max=M;//這里的;讓if else語(yǔ)句分開了本來(lái)是if(a)
else max=M
max=0; else max=0;
但是有一種用法
#define CASE breakcase
int main()
{
int a = 5;
switch (a)
{
case 1:
CASE 2 :
CASE 3 :
CASE 4 :
CASE 5 :
CASE 6 :
break;
}
return 0;
}
3.1define 定義宏 (宏里面的參數(shù)是整體替換的,并不會(huì)算出一個(gè)結(jié)果)
#define 機(jī)制包括了?個(gè)規(guī)定,允許把參數(shù)替換到文本中,這種實(shí)現(xiàn)通常稱為宏(macro)或定義宏 (define macro)。
#define name(參數(shù))替換的內(nèi)容 (空格隔開)
#define ADD(n) ((n)*(n)) 這里的括號(hào)盡量不要省,不然容易錯(cuò)誤
#define SQUARE(n) n+n
int main()
{
int ret = 10 * SQUARE(5); **結(jié)果是55不是100,變成了10*5+5**
printf("%d", ret);
return 0;
3.2 帶有副作用的宏參數(shù)
當(dāng)宏參數(shù)在宏的定義中出現(xiàn)超過(guò)?次的時(shí)候,如果參數(shù)帶有副作?,那么你在使?這個(gè)宏的時(shí)候就可 能出現(xiàn)危險(xiǎn),導(dǎo)致不可預(yù)測(cè)的后果。副作?就是表達(dá)式求值的時(shí)候出現(xiàn)的永久性效果。
#define MAX(a,b) (a)>(b)?(a):(b)
int main()
{
int a = 10;
int b = 20;
int ret = MAX(a++, b++);
printf("ret=%d,b=%d", ret, b);21,22
return 0;
}
4# 和##
4.1#(只能出現(xiàn)在宏體)
4.2##(記號(hào)粘合)
5.1#undef
#undef NAME (取消定義)
5.2 條件編譯
調(diào)試性的代碼,刪除可惜,保留?礙事,所以我們可以選擇性的編譯。
6.頭文件包含
(1):本地文件包含(一般這指自己創(chuàng)建的頭文件的包含) #include “test.h”
> 查找策略:先在源文件所在目錄下查找,如果該頭?件未找到,編譯器就像查找?guī)旌瘮?shù)頭文件?樣在標(biāo)準(zhǔn)位置查找頭文件。
> (先在自己這個(gè)文件目錄下找,找不到去庫(kù)函數(shù)里面找)
> 如果找不到就提示編譯錯(cuò)誤。
> Linux環(huán)境的標(biāo)準(zhǔn)頭?件的路徑: /usr/include
> VS環(huán)境的標(biāo)準(zhǔn)頭文件的路徑:
> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include
> //這是VS2013的默認(rèn)路徑 注意按照??的安裝路徑去找
(2):庫(kù)文件包含 #include
查找頭文件直接去標(biāo)準(zhǔn)路徑下去查找,如果找不到就提示編譯錯(cuò)誤。
這樣是不是可以說(shuō),對(duì)于庫(kù)文件也可以使? “” 的形式包含?
答案是肯定的,可以,但是這樣做查找的效率就低些,當(dāng)然這樣也不容易區(qū)分是庫(kù)文件還是本地文件了。
7.如何避免重復(fù)包含同一個(gè)頭文件:
每次編譯都要?jiǎng)h除#include,然后用包含頭文件的內(nèi)容替換,頭文件包含10次,替換10次,test.h文件的內(nèi)容被包含在test(如果test.h文件比較大,這樣預(yù)處理后代碼量會(huì)劇增。如果工程比較大,有公共使用的頭文件、被都能使用,又不做任何的處理,**那么后果真的不堪設(shè)想**。
如何解決頭文件被重復(fù)引入的問(wèn)題? 答案:條件編譯。 每個(gè)頭文件的開頭寫:
方法一:這種方法通過(guò)檢查一個(gè)特定的宏是否已經(jīng)被定義來(lái)決定是否包含頭文件的內(nèi)容.如果宏已經(jīng)定義(意味著頭文件已經(jīng)被包含過(guò)一次),則跳過(guò)頭文件的內(nèi)容。
#ifndef __TEST_H__
#define __TEST_H__
#endif //__TEST_H__
這種方法通過(guò)檢查一個(gè)特定的宏是否已經(jīng)被定義來(lái)決定是否包含頭文件的內(nèi)容.
如果宏已經(jīng)定義(意味著頭文件已經(jīng)被包含過(guò)一次),則跳過(guò)頭文件的內(nèi)容。
方法二: #pragma once提供了另一種更簡(jiǎn)潔的方法來(lái)實(shí)現(xiàn)同樣的功能。使用#pragma once,編譯器在遇到這個(gè)指令時(shí)會(huì)確保當(dāng)前頭文件在同一個(gè)編譯單元中只被包含一次,無(wú)論它是否被多次顯式包含。
#pragma once
與宏定義保護(hù)相比,#pragma once的優(yōu)點(diǎn)是:
更簡(jiǎn)潔,不需要定義和檢查宏。減少了命名沖突的風(fēng)險(xiǎn),因?yàn)椴恍枰獮槊總€(gè)頭文件創(chuàng)建一個(gè)唯一的宏名。在某些情況下,可以稍微提高編譯速度,因?yàn)榫幾g器可能能夠更有效地優(yōu)化包含關(guān)系
但是:需要注意的是,#pragma once是非標(biāo)準(zhǔn)的,這意味著它的行為可能不是所有編譯器都一致。但幸運(yùn)的是,幾乎所有現(xiàn)代主流編譯器都支持這個(gè)指令,并且其行為也基本一致。因此,在大多數(shù)情況下,#pragma once是一個(gè)安全且方便的選擇。
上述就是編譯(包括預(yù)處理詳解)和鏈接的全部?jī)?nèi)容了 能看到這里相信您一定對(duì)小編的文章有了一定的認(rèn)可,有什么問(wèn)題歡迎各位大佬指出 歡迎各位大佬評(píng)論區(qū)留言修正 您的支持就是我最大的動(dòng)力
柚子快報(bào)邀請(qǐng)碼778899分享:c語(yǔ)言 C--編譯和鏈接見解
推薦鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。