在ANSI C的任何一種實現(xiàn)中,存在兩個不同的環(huán)境。
第1種是翻譯環(huán)境,在這個環(huán)境中源代碼被轉(zhuǎn)換為可執(zhí)行的機器指令。
第2種是執(zhí)行環(huán)境,它用于實際執(zhí)行代碼
翻譯環(huán)境:
成都創(chuàng)新互聯(lián)公司長期為千余家客戶提供的網(wǎng)站建設服務,團隊從業(yè)經(jīng)驗10年,關注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務;打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為天門企業(yè)提供專業(yè)的成都做網(wǎng)站、網(wǎng)站建設,天門網(wǎng)站改版等技術服務。擁有10余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。展示了在Linux系統(tǒng)下程序的翻譯過程
1. 預處理 選項 gcc -E test.c -o test.i
預處理完成之后就停下來,預處理之后產(chǎn)生的結(jié)果都放在test.i文件中。
2. 編譯 選項 gcc -S test.c
編譯完成之后就停下來,結(jié)果保存在test.s中。
3. 匯編 gcc -c test.c
匯編完成之后就停下來,結(jié)果保存在test.o中。
4.鏈接過程是根據(jù)程序所用函數(shù)的名稱,將C函數(shù)庫或其他函數(shù)庫中的函數(shù)鏈接到程序中,最終形成可執(zhí)行文件。
運行環(huán)境:
程序執(zhí)行的過程:預處理指令 預定義符號
1. 程序必須載入內(nèi)存中。在有操作系統(tǒng)的環(huán)境中:一般這個由操作系統(tǒng)完成。在獨立的環(huán)境中,程序
的載入必須由手工安排,也可能是通過可執(zhí)行代碼置入只讀內(nèi)存來完成。
2. 程序的執(zhí)行便開始。接著便調(diào)用main函數(shù)。
3.開始執(zhí)行程序代碼。這個時候程序?qū)⑹褂靡粋€運行時堆棧(stack),存儲函數(shù)的局部變量和返回
地址。程序同時也可以使用靜態(tài)(static)內(nèi)存,存儲于靜態(tài)內(nèi)存中的變量在程序的整個執(zhí)行過程
一直保留他們的值。
4. 終止程序。正常終止main函數(shù);也有可能是意外終止。
__FILE__ //進行編譯的源文件
__LINE__ //文件當前的行號
__DATE__ //文件被編譯的日期
__TIME__ //文件被編譯的時間
__STDC__ //如果編譯器遵循ANSI C,其值為1,否則未定義
用法:
printf("file:%s",__FILE__); //輸出當前編譯的源文件名
printf("line:%d",__LINE__); //輸出當前行號
printf("date:%s",__DATE__); //輸出文件被編譯的日期
printf("time:%s",__TIME__); //輸出文件被編譯的時間
#define的用法定義標識符
用法:
#define 名字 被定義的具體內(nèi)容
如:
#define MAX 100
#define name "張三"
定義宏
在 C 語言中,可以采用命令 #define 來定義宏。該命令允許把一個名稱指定成任何所需的文本,例如一個常量值或者一條語句。在定義了宏之后,無論宏名稱出現(xiàn)在源代碼的何處,預處理器都會把它用定義時指定的文本替換掉。 慣例將宏名稱每個字母采用大寫,這有助于區(qū)分宏與一般的變量。
用法:
#define 名字(參數(shù)列表) 被定義的具體內(nèi)容
例:
#define MAX(a,b) (a>b?a:b)
注意事項:
參數(shù)列表的左括號必須與name緊鄰。
如果兩者之間有任何空白存在,參數(shù)列表就會被解釋為stuff的一部分
在程序中,如果我們想求出兩個數(shù)中的大值,通常一個方法就是編寫函數(shù)Max,計算結(jié)果并輸出;
同樣,該程序也可以采用定義宏來計算結(jié)果。
另外,為避免宏使用時出現(xiàn)爭議性錯誤,還應該注意它定義時的規(guī)范性
例如:
定義一個宏,求一個數(shù)的平方
#define SQUARE( x ) x * x
當程序中出現(xiàn)
int a = 5;
printf("%d\n" ,SQUARE( a + 1) );
由于宏是直接替換,參數(shù)x被替換成a+1,所以該語句實際上是printf("%d\n",a+1*a+1);
代入a=5,經(jīng)計算結(jié)果為11,并非主觀上的6*6=36
經(jīng)修正:
宏應該被定義為
#define SQUARE( x ) (x) * (x)
所以用于對數(shù)值表達式進行求值的宏定義都應該考慮充分,在合適的位置加上括號,避免在使用宏時由于參數(shù)中的操作符或鄰近操作符之間不可預料的相互作用。
#和##的用法通過觀察發(fā)現(xiàn),C語言的字符串有自動連接的特點,即兩個“”緊密相連的字符串可以一并輸出
#define PRINT(FORMAT, VALUE)\
printf("the value is "FORMAT"\n", VALUE);
...
PRINT("%d", 10); //輸出結(jié)果是什么
這里的宏參數(shù)為“%d”和10,分別替換FORMAT 和 VALUE,其中FORMAT在傳參時必須傳入帶有“”的參數(shù),否則便不能輸出。
使用 # ,把一個宏參數(shù)變成對應的字符串
上面代碼可更改為
#define PRINT(FORMAT, VALUE) printf("the value is "#FORMAT"\n", VALUE);
....
PRINT(%d, 10);
#FORMAT被預處理器預處理為"FORMAT"--->"%d"
##可以把位于它兩邊的符號合成一個符號。帶副作用的宏參數(shù)
它允許宏定義從分離的文本片段創(chuàng)建標識符。
#define ADD_TO_SUM(num, value) sum##num += value;
...
ADD_TO_SUM(5, 10);
//先進行宏替換-->sum##5+=10 ##將兩邊的符號合并-->sum5+=10 -->給sum5增加10.
當宏參數(shù)在宏的定義中出現(xiàn)超過一次的時候,如果參數(shù)帶有副作用,那么你在使用這個宏的時候就可能
出現(xiàn)危險,導致不可預測的后果。副作用就是表達式求值的時候出現(xiàn)的永久性效果。
例如:
x+1 //無副作用,x沒有變化
x++ //有副作用,x本身會+1
思考下面宏替換后輸出的結(jié)果是什么?
#define MAX(a, b) ( (a) >(b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);//輸出的結(jié)果是什么?
//6 9 9
宏替換后,MAX(x++,y++)-->((x++)>(y++)?(x++):(y++))-->
((5++)>(8++)?(5++):(8++))-->9
在使用宏定義時 盡量不要使用 x++ 等會影響原來參數(shù)的值的宏參數(shù)
宏和函數(shù)的對比屬性 | #define定義宏 | 函數(shù) |
代碼長度 | 程序中每次定義宏時,都會將宏代碼替換到程序中。若宏的代碼過長且程序中出現(xiàn)多段宏,則會增加代碼長度。 | 函數(shù)的代碼只出現(xiàn)在一個地方,每次函數(shù)調(diào)用都會調(diào)用同一個地方的代碼,多次調(diào)用函數(shù)不會增加代碼長度。 |
執(zhí)行速度 | 更快 | 函數(shù)的調(diào)用和返回消耗一定時間,會稍慢 |
操作符優(yōu)先級 | 宏參數(shù)的求值是在所有周圍表達式的上下文環(huán)境里, 除非加上括號,否則鄰近操作符的優(yōu)先級可能會產(chǎn)生 不可預料的后果,所以建議宏在書寫的時候多些括 號。 | 函數(shù)參數(shù)只在函數(shù)調(diào)用的時候求值一次,它的結(jié)果值傳遞給函數(shù)。表達式的求值結(jié)果更容易預測。 |
帶副作用的參數(shù) | 參數(shù)會被替換到宏的多個位置,所以應該避免使用帶副作用的參數(shù) | 函數(shù)參數(shù)只在函數(shù)調(diào)用時求值一次,結(jié)果更易預測 |
參數(shù)類型 | 宏的參數(shù)與類型無關,只要對參數(shù)的操作是合法的,它就可以使用于任何參數(shù)類型。 | 函數(shù)的參數(shù)是與類型有關的,參數(shù)的類型不同,就需要不同 的函數(shù)。 |
調(diào)試 | 不方便 | 方便 |
遞歸 | 不可遞歸 | 可遞歸 |
移除前面#define NAME的宏定義
#undef NAME條件編譯
//如果現(xiàn)存的一個名字需要被重新定義,那么它的舊名字首先要被移除。
在編譯一個程序的時候我們?nèi)绻獙⒁粭l語句(一組語句)編譯或者放棄是很方便的。因為我們有條件
編譯指令。比如在程序測試時想添加一些代碼,但測試過后不想刪除,可以采用更改其為注釋的方法,也可以使用條件編譯,僅讓其在測試時編譯。
常見的條件編譯指令:
1.單分支
#if 常量表達式
....
#endif
....
#define __DEBUG__ 1
#if __DEBUG__
printf("測試中。。?!?;
#endif
printf("測試完畢”);
2.多個分支的條件編譯
#if 常量表達式
...
#elif 常量表達式
...
#else
...
#endif
3.判斷是否被定義
//如果定義了symbol
#if defined(symbol)
#ifdef symbol
//如果沒有定義symbol
#if !defined(symbol)
#ifndef symbol
#define symbol 100
#ifdef symbol
printf("%d",symbol);
#ifndef symbol
printf("not define");
4.嵌套指令
#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif
你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧