C語言課件:第14章 預處理器_第1頁
C語言課件:第14章 預處理器_第2頁
C語言課件:第14章 預處理器_第3頁
C語言課件:第14章 預處理器_第4頁
C語言課件:第14章 預處理器_第5頁
已閱讀5頁,還剩51頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、1第14章預處理器引言指令如#define 和#include 等是由預處理器進行處理的,而預處理器是一個在編譯前編輯C程序的軟件.C語言(和 C+語言)因為依賴預處理器而不同于其他的編程語言。預處理器是一個強大的工具,但它同時也可能是許多難以發(fā)現(xiàn)的錯誤的根源.214.1 預處理器的工作方式預處理器查找以#開頭的預處理指令.#define 和#include指令.3預處理器的工作方式#define 指令定義一個宏( macro)即一個能夠代表其它東西的名字,比如一個常量。預處理器會通過將宏的名字和它的定義存儲在一起來響應#define指令當這個宏在后面的程序中使用到時,預處理器“擴展”了宏,將

2、宏替換為它所定義的值。4預處理器的工作方式#include指令告訴預處理器打開一個特定的文件,將它的內容作為正在編譯的文件的一部分“包含”進來。例如,下面一行:#include 指示預處理器打開一個名字為 stdio.h的文件,并將它的內容加到當前的程序中。5預處理器的工作方式在編譯過程中中,預處理器的角色如圖所示:6預處理器的工作方式預處理器的輸入是一個 C語言程序,程序可能包含指令。預處理器會執(zhí)行這些指令,并在處理過程中刪除這些指令。預處理器的輸出被直接交給編譯器。7預處理器的工作方式第二章中的 celsius.c :/* Converts a Fahrenheit temperature

3、 to Celsius */#include #define FREEZING_PT 32.0f#define SCALE_FACTOR (5.0f / 9.0f)int main(void) float fahrenheit, celsius; printf(Enter Fahrenheit temperature: ); scanf(%f, &fahrenheit); celsius = (fahrenheit - FREEZING_PT) * SCALE_FACTOR; printf(Celsius equivalent is: %.1fn, celsius); return 0;8預處

4、理器的工作方式預處理過后為:Blank lineBlank lineLines brought in from stdio.hBlank lineBlank lineBlank lineBlank lineint main(void) float fahrenheit, celsius; printf(Enter Fahrenheit temperature: ); scanf(%f, &fahrenheit); celsius = (fahrenheit - 32.0f) * (5.0f / 9.0f); printf(Celsius equivalent is: %.1fn, celsiu

5、s); return 0;9預處理器的工作方式預處理器不僅僅是執(zhí)行了指令,還做了一些其他的事情。特別值得注意的是,它將每一處注釋都替換為一個空格字符。有一些預處理器還會進一步刪除不必要的空白字符,包括在每一行開始用于縮進的空格符和制表符。10預處理器的工作方式注意:預處理器僅知道少量 C語言的規(guī)則,在執(zhí)行指令時非常有可能產生非法的程序。對于較復雜的程序,檢查預處理器的輸出可能是找到這類錯誤的有效途徑。 1114.2 預處理指令大多數(shù)預處理指令屬于下面 3種類型之一:宏定義。#define指令定義一個宏, #undef指令刪除一個宏定義。 文件包含。 #include指令導致一個指定文件的內容被

6、包含到程序中。 條件編譯。 #if、#ifdef、#ifndef、#elif、#else和#endif指令可以根據(jù)測試的條件來將一段文本塊包含到程序中或排除在程序之外。12預處理指令幾條應用于所有指令規(guī)則:指令都以#開始。#符號不需要在一行的行首,只要它之前只有空白字符就行。在 #后是指令名,接著是指令所需要的其他信息。 在指令的符號之間可以插入任意數(shù)量的空格或橫向制表符。例如,下面的指令是合法的: # define N 100 13預處理指令指令總是在第一個換行符處結束,除非明確地指明要繼續(xù)。如果想在下一行繼續(xù)指令,必須在當前行的末尾使用字符,例如:#define DISK_CAPACITY

7、 (SIDES * TRACKS_PER_SIDE * SECTORS_PER_TRACK * BYTES_PER_SECTOR)14預處理指令指令可以出現(xiàn)在程序中任何地方通常將 #define和#include指令放在文件的開始,其他指令則放在后面,甚至在函數(shù)定義的中間。注釋可以與指令放在同一行。實際上,在一個宏定義的后面加一個注釋來解釋宏的意義是一種比較好的習慣:#define FREEZING_PT 32.0f /* freezing point of water */1514.3 宏定義簡單的宏,沒有參數(shù)。帶參數(shù)的宏。16簡單的宏簡單的宏定義有如下格式:#define identifi

8、er replacement-list #define 標識符 替代列表replacement-list是一系列的 C語言記號,包括標識符、關鍵字、數(shù)、字符常量、字符串字面量、運算符和標點符號。在文件后面的內容中,不管identifier在任何位置出現(xiàn),預處理器都會用replacement-list代替它。17簡單的宏不要在宏定義中放置任何額外的符號,否則它們會被作為替換列表的一部分。一種常見的錯誤是在宏定義中使用 = ,如:#define N = 100 /* WRONG */int aN; /* becomes int a= 100; */18簡單的宏在宏定義的末尾使用分號結尾是另一個常見

9、錯誤,如:#define N 100; /* WRONG */int aN; /* becomes int a100; */19簡單的宏簡單的宏主要用來定義那些被稱為“明示常量”的東西也就是數(shù)值、字符值和字符串值:#define STR_LEN 80#define TRUE 1#define FALSE 0#define PI 3.14159#define CR r#define EOS 0#define MEM_ERR Error: not enough memory20標識符通常用大寫,以示區(qū)別!簡單的宏使用#define來為常量命名的優(yōu)點有:使程序更易于閱讀。名字可以幫助讀者理解常量的意

10、義。程序會更易于修改。我們只需要改變一個宏定義,就可以改變整個程序中出現(xiàn)的所有該常量的值??梢詭椭苊馇昂蟛灰恢禄蜴I盤輸入錯誤。假如數(shù)值常量3.14159在程序中大量出現(xiàn),它可能被意外地誤寫為3.1416 或者3.14195。21帶參數(shù)的宏帶參數(shù)的宏定義有如下格式:#define 標識符(x1, x2, xn) 替換列表 其中 x1, x2, xn是標識符(宏的參數(shù))。在宏的名字和左括號之間必須沒有空格,否則會認為是在定義一個簡單的宏,其中(x1, x2, xn)是替換列表的一部分。22帶參數(shù)的宏當預處理器遇到一個帶參數(shù)的宏,會將定義存儲起來以便后面使用。在后面的程序中,如果任何地方出現(xiàn)了標識

11、符(y1, y2, yn)格式的宏調用(其中 y1, y2, yn是一系列標記),預處理器會使用替換列表替代,并使用 y1替換 x1,y2替換 x2,依此類推。帶參數(shù)的宏經常用來作為一些簡單的函數(shù)使用。23帶參數(shù)的宏帶參數(shù)的宏舉例:#define MAX(x,y) (x)(y)?(x):(y)#define IS_EVEN(n) (n)%2=0)調用這些宏:i = MAX(j+k, m-n);if (IS_EVEN(i) i+;宏替換后:i = (j+k)(m-n)?(j+k):(m-n);if (i)%2=0) i+;24帶參數(shù)的宏復雜的類似函數(shù)的宏:#define TOUPPER(c) (

12、a=(c)&(c)=z?(c)-a+A:(c) 頭提供了一個更容易移植的相似函數(shù)toupper。帶參數(shù)的宏也可能具有空的參數(shù)列表:#define getchar() getc(stdin)空的參數(shù)列表不是必需的,但可以使 getchar更像一個函數(shù)。25帶參數(shù)的宏使用帶參數(shù)的宏替代實際的函數(shù)有兩個優(yōu)點:程序可能會稍微快些。一個函數(shù)調用在執(zhí)行時通常會有些額外開銷 存儲上下文信息、復制參數(shù)的值等。而一個宏的調用則沒有這些運行開銷。宏會更“通用”。與函數(shù)的參數(shù)不同,宏的參數(shù)沒有類型。只要預處理后的程序依然是合法的,宏可以接受任何類型的參數(shù)。26帶參數(shù)的宏帶參數(shù)的宏也有一些缺點。編譯后的代碼通常會變大

13、。每一處宏調用都會導致插入宏的替換列表,由此導致程序的源代碼增加 (因此編譯后的代碼變大)。當宏調用嵌套時,這個問題會相互疊加從而使程序更加復雜。n = MAX(i, MAX(j, k);預處理后的這條語句:n = (i)(j)(k)?(j):(k)?(i):(j)(k)?(j):(k);27帶參數(shù)的宏帶參數(shù)的宏也有一些缺點宏參數(shù)沒有類型檢查。無法用一個指針來指向一個宏。28帶參數(shù)的宏宏可能會不止一次地計算它的參數(shù)。如果參數(shù)有副作用,多次計算參數(shù)的值可能會產生意外的結果。n = MAX(i+, j);這條語句在預處理之后的結果:n = (i+)(j)?(i+):(j);如果 i大于 j,那么

14、i可能會被(錯誤地)增加了兩次,同時 n可能被賦予了錯誤的值。29為了自保護,最好避免使用帶有副作用的參數(shù)。帶參數(shù)的宏帶參數(shù)的宏經常用作需要重復書寫的代碼段模式。顯示整數(shù)的一種更容易的宏:#define PRINT_INT(n) printf(%dn, n)預處理器將把下行PRINT_INT(i/j);轉換為printf(%dn, i/j);30#運算符宏定義可以包含兩個運算符:#和#。#運算符將一個宏的參數(shù)轉換為字符串字面量。它僅允許出現(xiàn)在帶參數(shù)的宏的替換列表中。31#運算符改進后的 PRINT_INT:#define PRINT_INT(n) printf(#n = %dn, n)調用PR

15、INT_INT(i/j);將轉換為printf(i/j = %dn, i/j);編譯器自動連接兩個相鄰的字符串字面量,因此上面語句等價為:printf(i/j = %dn, i/j);32#運算符#運算符可以將兩個記號(例如標識符)“粘”在一起,成為一個記號。如果其中一個操作數(shù)是宏參數(shù),“粘合”會在當形式參數(shù)被相應的實際參數(shù)替換后發(fā)生。33#運算符使用# 運算符的宏:#define MK_ID(n) i#n下面聲明調用 MK_ID 三次:int MK_ID(1), MK_ID(2), MK_ID(3);預處理過后,該語句為:int i1, i2, i3;34宏的通用屬性同時適用于簡單和帶參數(shù)的

16、宏的規(guī)則:宏的替換列表可以包含對另一個宏的調用。例如:#define PI 3.14159#define TWO_PI (2*PI)當預處理器在后面的程序中遇到 TWO_PI時,會將它替換成(2*PI)。預處理器會不斷重新檢查替換列表,直到將所有的宏名字都替換掉為止。 35宏的通用屬性預處理器只會替換完整的記號,而不會替換記號的片斷。因此,預處理器會忽略嵌在標識符名、字符常量、字符串字面量之中的宏名。例如:#define SIZE 256int BUFFER_SIZE;if (BUFFER_SIZE SIZE) puts(Error: SIZE exceeded);預處理過后:int BUFF

17、ER_SIZE;if (BUFFER_SIZE 256) puts(Error: SIZE exceeded);36宏的通用屬性一個宏定義的作用范圍通常到出現(xiàn)這個宏的文件末尾。一個定義在函數(shù)中的宏并不是僅在函數(shù)內起作用,而是作用到文件末尾。宏不可以被定義兩遍,除非新的定義與舊的定義是一樣的。37宏的通用屬性宏可以使用 #undef指令“取消定義”。#undef指令有如下形式: #undef 標識符 其中標識符是宏名#undef指令的一個用途是取消一個宏的現(xiàn)有定義,以便于重新給出新的定義。38宏定義中的圓括號宏定義中的替換列表往往需要圓括號,以免發(fā)生意料之外的結果。如果宏的替換列表中有運算符,那

18、么始終要將替換列表放在括號中:#define TWO_PI (2*3.14159)如果宏有參數(shù),每次參數(shù)在替換列表中出現(xiàn)時都要放在圓括號中:#define SCALE(x) (x)*10)沒有括號的話,編譯器可能會不按我們期望的方式應用運算符的優(yōu)先級和結合性規(guī)則。39宏定義中的圓括號考慮下面的宏定義,其中的替換列表沒有添加圓括號: #define TWO_PI 2*3.14159/* needs parentheses around replacement list */預處理過程中,語句:conversion_factor = 360/TWO_PI;成為conversion_factor =

19、 360/2*3.14159;除法會在乘法之前完成,產生期望之外的結果。40宏定義中的圓括號當宏有參數(shù)時,僅給替換列表添加圓括號是不夠的。參數(shù)的每一次出現(xiàn)都要添加圓括號。#define SCALE(x) (x*10) /* needs parentheses around x */在預處理過程中,語句j = SCALE(i+1);變?yōu)閖 = (i+1*10);由于乘法的優(yōu)先級比加法高,這條語句等價于:j = i+10;4114.4 條件編譯C語言的預處理器可以識別大量用于支持條件編譯的指令。預處理器根據(jù)條件測試結果來決定包含或排除程序中的一些片斷。42#if指令和#endif指令假如我們正在調

20、試一個程序。我們想要程序顯示出特定變量的值,因此將 printf函數(shù)調用添加到程序中重要的部分。一旦找到錯誤,經常需要保留這些 printf函數(shù)調用,以備以后使用。條件編譯允許我們保留這些調用,但是讓編譯器忽略它們。43#if指令和#endif指令首先定義一個宏,并給它一個非 0的值:#define DEBUG 1接下來,我們要在每組 printf函數(shù)調用的前后加上#if和#endif:#if DEBUGprintf(Value of i: %dn, i);printf(Value of j: %dn, j);#endif44#if指令和#endif指令在預處理過程中,# if指令會測試 DE

21、BUG的值。由于 DEBUG的值非0,因此預處理器會將這兩個 printf函數(shù)調用保留在程序中(但#if和#endif行會消失)。如果將 DEBUG的值改為 0并重新編譯程序,預處理器則會將這 4行代碼都刪除??梢詫?if-#endif保留在最終的程序中,這樣如果程序在運行時出錯,可以繼續(xù)產生這些診斷信息(將 DEBUG改為 1并重新編譯)。45#if指令和#endif指令一般來說,#if指令的格式如下:#if constant-expression#endif當預處理器遇到#if指令時,會計算常量表達式。如果表達式的值為0,那么在# if與 #endif之間的行將在預處理過程中從程序中刪除。

22、否則,這些在# if和#endif之間的行會被保留在程序中,并繼續(xù)被編譯器處理這時#if和#endif對程序沒有任何影響。46#if指令和#endif指令對于沒有定義過的標識符,#if指令會把它當作是值為 0的宏對待。如果我們沒有定義 DEBUG, 則測試:#if DEBUG將會失?。ǖ粫a生錯誤消息)。而測試:#if !DEBUG則會成功。47defined運算符預處理器支持三種運算符: #, #, 和defined。當defined應用于標識符時,如果標識符是一個當前定義過的宏則返回1,否則返回0。# defined運算符通常與#if指令結合使用。48defined運算符例子:#if d

23、efined(DEBUG)#endif僅當 DEBUG被定義成宏時,# if和#endif之間的代碼會被保留在程序中。DEBUG兩側的括號不是必需的,因此可以簡單寫成:#if defined DEBUG也不必給 DEBUG 一個值:#define DEBUG49#ifdef指令和#ifndef指令#ifdef指令測試一個標識符是否已經定義為宏:#ifdef identifier其效果與下面語句相同:#if defined(identifier)#ifndef 指令測試一個標識符當前是否未被定義為宏:#ifndef identifier其效果與下面語句相同:#if !defined(identifier)50#ifdef指令

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論