C語言深入探究程序的編譯之預(yù)處理
1.程序的翻譯環(huán)境和執(zhí)行環(huán)境
在ANSI C中任何一種實(shí)現(xiàn)中,存在兩個(gè)不同的環(huán)境.
第一種是翻譯環(huán)境,在這個(gè)環(huán)境中源代碼被轉(zhuǎn)換為可執(zhí)行的機(jī)器指令
第二種執(zhí)行環(huán)境,它用于實(shí)際執(zhí)行代碼。
2.詳解編譯與鏈接
2.1翻譯環(huán)境
1.組成一個(gè)程序的每個(gè)源文件通過編譯過程分別轉(zhuǎn)換成目標(biāo)代碼(object code)。
2.每個(gè)目標(biāo)文件由鏈接器(linker)捆綁在一起,形成一個(gè)單一而完整的可執(zhí)行程序。
3.鏈接器同時(shí)也會(huì)引入標(biāo)準(zhǔn)C函數(shù)庫(kù)中任何被該程序所用到的函數(shù),而且它可以搜索程序員個(gè)人的程序庫(kù),將其需要的函數(shù)也鏈接到程序中。
2.2編譯本身也分為幾個(gè)階段
2.3運(yùn)行環(huán)境
程序執(zhí)行的過程:
1. 程序必須載入內(nèi)存中。在有操作系統(tǒng)的環(huán)境中:一般這個(gè)由操作系統(tǒng)完成。在獨(dú)立的環(huán)境中,程序 的載入必須由手工安排,也可能是通過可執(zhí)行代碼置入只讀內(nèi)存來完成。
2. 程序的執(zhí)行便開始。接著便調(diào)用main函數(shù)
3.開始執(zhí)行程序代碼。這個(gè)時(shí)候程序?qū)⑹褂靡粋€(gè)運(yùn)行時(shí)堆棧(stack),存儲(chǔ)函數(shù)的局部變量和返回 地址。程序同時(shí)也可以使用靜態(tài)(static)內(nèi)存,存儲(chǔ)于靜態(tài)內(nèi)存中的變量在程序的整個(gè)執(zhí)行過程一直保留他們的值。
4. 終止程序。正常終止main函數(shù);也有可能是意外終止
3.預(yù)處理詳解
3.1預(yù)處理符號(hào)
__FILE__ | //進(jìn)行編譯的源文件 |
__LINE__ | //文件當(dāng)前的行號(hào) |
__DATE__ | //文件被編譯的日期 |
__TIME__ | //文件被編譯的時(shí)間 |
__STDC__ | //如果編譯器遵循ANSI C,其值為1,否則未定義 |
3.2#define
3.2.1#define定義標(biāo)識(shí)符
//語法 #define name stuff //舉例 #define MAX 1000 #define reg register //為 register這個(gè)關(guān)鍵字,創(chuàng)建一個(gè)簡(jiǎn)短的名字 #define do_forever for(;;) //用更形象的符號(hào)來替換一種實(shí)現(xiàn) #define CASE break;case //在寫case語句的時(shí)候自動(dòng)把 break寫上。 // 如果定義的 stuff過長(zhǎng),可以分成幾行寫,除了最后一行外,每行的后面都加一個(gè)反斜杠(續(xù)行符)。 #define DEBUG_PRINT printf("file:%s\tline:%d\t \ date:%s\ttime:%s\n" ,\ __FILE__,__LINE__ , \ __DATE__,__TIME__ )
3.2.2#define定義宏
#define 機(jī)制包括了一個(gè)規(guī)定,允許把參數(shù)替換到文本中,這種實(shí)現(xiàn)通常稱為宏(macro)或定義
宏(define macro)。
聲明方式:
#define name( parament-list ) stuff
舉例:
#define ADD(x) x+x int a = 5; ADD(a) //等價(jià)于 //ADD(a)=5+5
3.2.3#define的替換規(guī)則
在程序中擴(kuò)展#define定義符號(hào)和宏時(shí),需要涉及幾個(gè)步驟。
1. 在調(diào)用宏時(shí),首先對(duì)參數(shù)進(jìn)行檢查,看看是否包含任何由#define定義的符號(hào)。如果是,它們首先 被替換。
2. 替換文本隨后被插入到程序中原來文本的位置。對(duì)于宏,參數(shù)名被他們的值所替換。
3. 最后,再次對(duì)結(jié)果文件進(jìn)行掃描,看看它是否包含任何由#define定義的符號(hào)。如果是,就重復(fù)上 述處理過程。
注意:
1. 宏參數(shù)和#define 定義中可以出現(xiàn)其他#define定義的符號(hào)。但是對(duì)于宏,不能出現(xiàn)遞歸。
2. 當(dāng)預(yù)處理器搜索#define定義的符號(hào)的時(shí)候,字符串常量的內(nèi)容并不被搜索。
3.2.4 宏和函數(shù)對(duì)比
宏通常被應(yīng)用于執(zhí)行簡(jiǎn)單的運(yùn)算。
比如在兩個(gè)數(shù)中找出較大的一個(gè)。
#define MAX(a, b) ((a)>(b)?(a):(b))
那為什么不用函數(shù)來完成這個(gè)任務(wù)?
原因有二:
1. 用于調(diào)用函數(shù)和從函數(shù)返回的代碼可能比實(shí)際執(zhí)行這個(gè)小型計(jì)算工作所需要的時(shí)間更多。 所以宏比函數(shù)在程序的規(guī)模和速度方面更勝一籌。
2. 更為重要的是函數(shù)的參數(shù)必須聲明為特定的類型。 所以函數(shù)只能在類型合適的表達(dá)式上使用。反之這個(gè)宏怎可以適用于整形、長(zhǎng)整型、浮點(diǎn)型等可以用>來比較的類型。
宏是類型無關(guān)的。
宏的缺點(diǎn):當(dāng)然和函數(shù)相比宏也有劣勢(shì)的地方:
1. 每次使用宏的時(shí)候,一份宏定義的代碼將插入到程序中。除非宏比較短,否則可能大幅度增加程序的長(zhǎng)度。
2. 宏是沒法調(diào)試的。
3. 宏由于類型無關(guān),也就不夠嚴(yán)謹(jǐn)。
4. 宏可能會(huì)帶來運(yùn)算符優(yōu)先級(jí)的問題,導(dǎo)致程容易出現(xiàn)錯(cuò)
宏和函數(shù)的一個(gè)對(duì)比:
屬性 | #define定義宏 | 函數(shù) |
---|---|---|
代碼長(zhǎng)度 | 每次使用時(shí),宏代碼都會(huì)被插入到程序中。除了非常小的宏之外,程序的長(zhǎng)度會(huì)大幅度增長(zhǎng) | 函數(shù)代碼只出現(xiàn)于一個(gè)地方;每次使用這個(gè)函數(shù)時(shí),都調(diào)用那個(gè)地方的同一份代碼 |
執(zhí)行速度 | 更快 | 存在函數(shù)的調(diào)用和返回的額外開銷,所以相對(duì)慢一些 |
操作符優(yōu)先級(jí) | 宏參數(shù)的求值是在所有周圍表達(dá)式的上下文環(huán)境里,除非加上括號(hào),否則鄰近操作符的優(yōu)先級(jí)可能會(huì)產(chǎn)生不可預(yù)料的后果,所以建議宏在書寫的時(shí)候多些括號(hào)。 | 函數(shù)參數(shù)只在函數(shù)調(diào)用的時(shí)候求值一次,它的結(jié)果值傳遞給函數(shù)。表達(dá)式的求值結(jié)果更容易預(yù)測(cè)。 |
帶有副作用的參數(shù) | 參數(shù)可能被替換到宏體中的多個(gè)位置,所以帶有副作用的參數(shù)求值可能會(huì)產(chǎn)生不可預(yù)料的結(jié)果。 | 函數(shù)參數(shù)只在傳參的時(shí)候求值一次,結(jié)果更容易控制。 |
參數(shù)類型 | 宏的參數(shù)與類型無關(guān),只要對(duì)參數(shù)的操作是合法的,它就可以使用于任何參數(shù)類型。 | 函數(shù)的參數(shù)是與類型有關(guān)的,如果參數(shù)的類型不同,就需要不同的函數(shù),即使他們執(zhí)行的任務(wù)是相同的。 |
調(diào)試 | 宏是不方便調(diào)試的 | 函數(shù)是可以逐語句調(diào)試的 |
遞歸 | 宏是不能遞歸的 | 函數(shù)是可以遞歸的 |
3.2.5命名約定
一般來講函數(shù)的宏的使用語法很相似。所以語言本身沒法幫我們區(qū)分二者。
那我們平時(shí)的一個(gè)習(xí)慣是:
把宏名全部大寫
函數(shù)名不要全部大寫
3.3#undef
這條指令用于移除一條宏定義
#undef NAME//如果現(xiàn)存的一個(gè)名字需要被重新定義,那么它的舊名字首先要被移除。
3.4條件編譯
在編譯一個(gè)程序的時(shí)候我們?nèi)绻獙⒁粭l語句(一組語句)編譯或者放棄是很方便的。因?yàn)槲覀冇袟l件編譯指令。
1.
#if 常量表達(dá)式
//...
#endif
//常量表達(dá)式由預(yù)處理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif
2.多個(gè)分支的條件編譯
#if 常量表達(dá)式
//...
#elif 常量表達(dá)式
//...
#else
//...
#endif
3.判斷是否被定義
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
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
到此這篇關(guān)于C語言深入探究程序的編譯之預(yù)處理的文章就介紹到這了,更多相關(guān)C語言預(yù)處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實(shí)現(xiàn)二叉樹非遞歸遍歷方法實(shí)例總結(jié)
這篇文章主要介紹了C++實(shí)現(xiàn)二叉樹非遞歸遍歷方法實(shí)例總結(jié),是算法設(shè)計(jì)中比較經(jīng)典的一個(gè)遍歷算法,需要的朋友可以參考下2014-08-08C++教程之a(chǎn)rray數(shù)組使用示例詳解
這篇文章主要為大家介紹了C++教程之a(chǎn)rray數(shù)組使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03