




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
C語言程序設計
—進階篇第9章編譯預處理和位運算內容概述不帶參數宏的定義及使用帶參數宏的定義及使用文件包含的概念位運算
教學目標1.了解編譯預處理的概念,了解宏的概念。2.了解文件包含的概念,熟練掌握文件包含命令include的用法。3.了解條件編譯的概念,熟悉常用的條件編譯命令及其用法。4.了解字節(jié)和位的有關概念,正確使用常見的位運算符和位運算操作;5.了解位段的要領及位段的使用方法◆預處理是指在進行編譯的第一遍掃描(詞法掃描、語法分析、代碼生成、代碼優(yōu)化)之前所做的工作。預處理是C語言的一個重要功能,也是C語言與其他高級語言的一個重要區(qū)別,它由預處理程序負責完成。當對一個源文件進行編譯時,系統(tǒng)將自動引用預處理程序對源程序中的預處理部分進行處理,處理完畢自動進入對源程序的編譯,將預處理的結果和源程序一起再進行通常的編譯處理,以得到目標代碼(OBJ文件)。
◆預處理命令以“#”號開頭,結束不加分號。預處理命令包括宏定義、文件包含和條件編譯。
C語言源程序中允許用一個標識符來表示一個較復雜的字符串,稱為“宏”,被定義為“宏”的標識符稱為“宏名”。在編譯預處理時,對程序中所有出現的“宏名”,都用宏定義中的字符串去代換,這稱為“宏代換”或“宏展開”。語法形式:#define標識符字符串#define為預編譯符。標識符稱為“宏名”,通常使用大寫英文字母和有直觀意義的標識符命名,以區(qū)別于源程序中的其它標識符。字符串可以是常數、表達式、格式串等。9.1.1不帶參數的宏定義9.1宏定義宏定義后,程序中宏名就代表了該字符串。程序在預編譯時,將源程序中出現的宏名PI替換為字符串“3.1415926”#include<stdio.h>#definePI3.1415926voidmain(){floatradius,length,area;
scanf("%f",&radius);length=PI*radius;area=PI*radius*radius;
printf("length=%.2f,area=%.2f\n",length,area);}【例9.1】使用無參宏定義圓周率,輸入圓的半徑,求圓的周長和面積。宏定義的作用:【例9.2】不帶參數的宏定義舉例。#defineM(y*y+3*y)#include"stdio.h"main(){
int
s,y;
printf("inputanumber:");
scanf("%d",&y);s=3*M+4*M+5*M;
printf("s=%d\n",s);}所有的M被(y*y+3*y)代替在宏定義中表達式(y*y+3*y)兩邊的括號不能少,否則會發(fā)生錯誤。(1)使用宏名代替一個字符串,可以增加程序的可讀性,而且用宏名代替不易出錯。(2)編譯預處理時,將程序中PI用3.1415926代替,與宏調用的過程相反,這種將宏名替換成字符串的過程稱為“宏展開”。關于宏定義的說明:(3)C語言中,用宏名替換一個字符串是簡單的轉換過程,不作語法檢查。
(4)宏定義命令行放在源程序的函數外時,宏名的作用域從宏定義命令行開始到本源文件結束。(5)宏名的作用域可以使用#undef命令終止,形式為:#undef
標識符(6)在宏定義中,允許在宏體字符串中使用已定義過的宏名,這個過程稱為嵌套宏定義。(8)對程序中用雙引號括起來的字符串,即使與宏名相同,也不替換。(7)宏定義不是說明或語句,在行末不必加分號。9.1.2帶參數的宏定義
1.帶參數宏的定義與調用定義形式:#define標識符(形式參數表)字符串◆形式參數稱為宏名的形式參數,簡稱形參;◆構成宏體的字符串中應該包含所指的形式參數;◆宏名與后續(xù)圓括號之間不能留空格。例如,宏定義與調用形式如下:
#defineM(y)y*y+3*y
/*宏定義*/
k=M(5);
/*宏調用*/◆帶參數宏調用的一般形式為
宏名(實參表);2.宏展開◆帶參數的宏展開是按#define命令行中指定的字符串從左到右進行置換?!羧绻牦w字符串中包含宏名中的形參,則將程序語句中相應的實參代替形參,如果字符串中的字符不是參數字符,則保留。例帶參數的宏定義#defineS(a,b)a*bmain(){
intarea;area=S(3,2);
printf("area=%d\n",area);}程序運行結果:area=6【例9.3】帶參數的宏定義舉例。#include"stdio.h"#defineMAX(a,b)(a>b)?a:bvoidmain(){int
x,y,max;
printf("inputtwonumbers:");
scanf("%d%d",&x,&y);max=MAX(x,y);
printf("max=%d\n",max);}說明
(1)帶參宏定義中,宏名和形參表之間不能有空格出現。例如,把“#defineMAX(a,b)(a>b)?a:b”寫為“#defineMAX(a,b)(a>b)?a:b”將被認為是無參宏定義,宏名MAX代表字符串(a,b)(a>b)?a:b。宏展開時,宏調用語句“max=MAX(x,y);”將變?yōu)椤癿ax=(a,b)(a>b)?a:b(x,y);”,這顯然是錯誤的。(2)在宏定義中的形參是標識符,而宏調用中的實參可以是表達式。但對于以下宏調用語句:
s=SQ(p+q)/*原意求(p+q)2*/a=AR(a+b,b+c)/*原意求矩形的面積(a+b)*(b+c)*/注意:若定義#defineSQ(n)(n)*(n)#defineAR(a,b)(a)*(b)此時宏展開上述兩個宏調用,結果為:宏替換是直接替換,必要時應在參數兩側加括號,在整個字符串外也應加括號。
若定義#defineSQ(n)n*n#defineAR(a,b)a*b此時宏展開上述兩個宏調用,結果為:s=p+q*p+qa=a+b*b+cs=(p+q)*(p+q)a=(a+b)*(b+c)練習:分析下面程序的運行結果
#defineSQ(y)(y)*(y)#include"stdio.h"voidmain(){int
a,sq;
printf("inputanumber:");
scanf("%d",&a);sq=SQ(a+1);
printf("sq=%d\n",sq);}#defineSQ(y)(y)*(y)#include"stdio.h"voidmain(){int
a,sq;
printf("inputanumber:");
scanf("%d",&a);sq=SQ(a+1);
printf("sq=%d\n",sq);}當輸入3時,以上兩段程序各輸出什么?3.帶參數的宏與函數的區(qū)別
帶參數的宏名與函數名相似,都是在宏名或函數名后跟一對圓括號;帶參數的宏調用形式與帶參數的函數的調用形式類似,都要求實參個數、次序與對應的形參一致。但兩者仍然存在著很大的不同?!纠?.5】帶參函數與帶參的宏的區(qū)別。/*帶參函數*/#include"stdio.h"voidmain(){inti=1;
while(i<=5)printf("%5d",SQ(i++));printf("\n");}SQ(inty){
return((y)*(y));}帶參的宏
#include"stdio.h"#defineSQ(y)((y)*(y))voidmain(){inti=1;
while(i<=5)printf("%5d",SQ(i++));printf("\n");}(1)宏定義與宏調用是為了減少書寫量和提高運行速度;而函數定義、函數調用是為了實現模塊程序設計,便于構造軟件。
(2)宏調用展開后的代碼是嵌入源程序中的,且每調用一次,嵌入一次代碼。因此,宏調用時總的程序代碼是增加的;而函數調用是執(zhí)行時轉入對應的函數,執(zhí)行后返回主調函數,無論調用多少次,函數體的代碼都不會增加。所以函數也解決代碼重用問題。
(3)宏定義的參數是字符,不需說明類型;而函數定義的參數是數據,不僅要說明其類型,而在調用時必須檢查實參與形參在類型上的一致性。(4)宏調用在編譯的預處理時完成宏展開;而函數調用在程序運行過程中被執(zhí)行。(5)除了將宏展開結果嵌入源程序外,宏調用不存在內存分配問題;而對函數可能需分配臨時空間以存放函數調用之結果。
◆編譯預處理的文件包含功能是一個源程序通過#include命令把另外一個源文件的全部內容嵌入到源程序中來。◆編譯預處理程序把#include命令行中所指定的源文件的全部內容放到源程序的#include命令行所在的位置?!粼诰幾g時并不是作為兩個文件聯接,而是作為一個源程序編譯,得到一個目標文件。9.2文件包含1#include命令格式首先對使用包含文件的源文件所在的目錄進行檢索,若沒有找到指定的文件,再在標準目錄中檢索。
#include”文件名”#include<文件名>只檢索C語言編譯系統(tǒng)所確定的標準目錄
2使用#include命令時的注意事項例如:(1)一個include命令只能指定一個被包含文件,如果要包含n個文件,用n個include命令。
(2)當一個程序中使用#include命令嵌入一個指定的包含文件時,被嵌入的文件中還可以使用#include命令,又可以包含另外一個指定的包含文件。
(3)如果文件file1.h中包含文件file2.h,而文件file2.h要用到文件file3.h的內容,則可在文件file1.h中用兩個include命令分別包含文件file2.h和文件file3.h,而且文件file3.h應出現在文件file2.h之前,即在文件file1.h中:
#include"file3.h"#include"file2.h"
使用條件編譯功能,為程序的調試和移植提供了有力的機制,使程序可以適應不同系統(tǒng)和硬件設置的通用性和靈活性。1#if命令功能:(1)單分支結構若表達式的值為真(非0),則編譯語句,否則語句不被編譯。其中語句可為若干C語言合法的語句和(或)命令行。9.3條件編譯#if常量表達式語句#endif(2)雙分支結構功能:若表達式的值為真(非0),則編譯語句1;否則編譯語句2。(3)多分支結構#if常量表達式語句1#else
語句2#endif#if常量表達式1
語句1#elif
常量表達式2
語句2…#elif
常量表達式n
語句n#else
語句n+1#endif2#ifdef命令
#ifdef
標識符語句
#endif或:#ifdef
標識符語句1#else
語句2#endif
(1)#ifdef命令行若標識符已定義,則編譯語句。若所指標識符已定義,則編譯語句1;否則編譯語句2。例如:#defineDEBUG#ifdefDEBUG
printf("x=%d,y=%d,z=%d\n",x,y,z);#endif
#defineR1main(){floatc,r,s;
printf("輸入圓的半徑或矩形的邊長:");
scanf("%f",&c);#ifdefRr=3.14*c*c;
printf("圓面積=%f\n",r);#elses=c*c;
printf("矩形面積=%f\n",s);#endif}【例9.6】根據需要,輸入圓的半徑或矩形的邊長,計算面積。#ifndef
標識符 語句#endif或形式為:#ifndef
標識符 語句1#else
語句2#endif(2)#ifndef命令行若標識符未定義,則編譯語句。若所指標識符未定義,則編譯語句1;否則編譯語句2。3#undef命令
形式:#undef
標識符其中#undef為預編譯符。功能:使所指標識符變?yōu)闊o定義。C語言既是一種高級語言,廣泛應用于應用軟件的開發(fā)和程序設計,同時又是一種低級語言,可以用于系統(tǒng)軟件的開發(fā)和程序設計,如自動控制系統(tǒng)中的過程控制、參數檢測、數據通訊等控制程序,都可以綜合利用C語言中的指針操作、位運算和位段技術來實現。9.4位運算
所謂位運算,是指參與運算的量,按對應的二進制位進行運算。按位運算是對字節(jié)或字中的實際位進行檢測、設置或移位,它只適用于整型和char型變量,對其他數據類型不適用。
位運算的特點:運算按二進制逐位進行——沒有借位和進位。9.4.1位運算和位運算符位運算符有以下六種:運算符名稱舉例優(yōu)先級~按位取反~flag(高)(算術運算符)<<左移a<<2>>右移b>>3(關系運算符)&按位與flag&0x37^按位異或flag^0xC4|按位或flag|0x5A(低)(賦值運算符)1.按位與運算:&運算規(guī)則0&0=0;0&1=0;1&0=0;1&1=1;用法按位清零保留某些指定位例#include<stdio.h>voidmain(){unsignedchara,b;
printf("Enteraandb:");
scanf("%o,%o",&a,&b);
printf("a&b=%o\n",a&b);}計算
010000(a)&011000(b)010000
001010(a)&010000(b)000000Enteraandb:20,30a&b=20Enteraandb:12,20a&b=02.按位或運算:|運算規(guī)則0|0=0;0|1=1;1|0=1;1|1=1;用法按位置1例#include<stdio.h>voidmain(){unsignedchara,b;
printf("Enteraandb:");
scanf("%o,%o",&a,&b);
printf("a|b=%o\n",a|b);}Enteraandb:20,30a|b=30Enteraandb:12,20a|b=32計算
010000(a)|011000(b)011000
001010(a)|010000(b)0110103.按位異或運算:^運算規(guī)則0^0=0;0^1=1;1^0=1;1^1=0;說明相“異”則為1,相“同”則為0相當于按位且無進位的加法例
以下程序的功能是將a數據的低4位取反。#include<stdio.h>voidmain(){unsignedchara=0x39,b=
;a=a^b;
printf("%x\n",a);}答案:0x0f
計算
00111001(a)^00001111(b)00110110與0相異或,保持原值不變與自身相異或,則全部位清零交換兩個整數值a=a^b;b=b^a;a=a^b;4.取反運算:~運算規(guī)則~0=1;~1=0;用法所有位翻轉獲得適用于不同系統(tǒng)的位運算模板例#include<stdio.h>voidmain(){ chara=3;
intb=10;
printf("~a=%d,~b=%d\n",~a,~b);}結果:~a=-4,~b=-11
計算~a:補碼:11111100原碼:10000100~b:補碼:11110101原碼:100010115.左移運算:<<運算規(guī)則i<<n把i各位全部向左移動n位最左端的n位被移出丟棄最右端的n位用0補齊用法若沒有溢出,則左移n位相當于乘上2n運算速度比真正的乘法和冪運算快得多例
以下程序的運行結果是
。#include<stdio.h>voidmain(){ unsignedinta=0x3ef,b; b=a<<2;
printf("%x,%x\n",a,b);}A)3ef,fbB)3ef,fbcC)fbc,3efD)fbc,fbc結果:B
例
以下程序的運行結果是
。#include<stdio.h>voidmain(){
inta=12,b; b=0x1f5&a<<3;
printf("%d,%d\n",a,b);}結果:12,96計算已知:0x1f5為111110101且:∵a為1100∴a<<3為1100000111110101&001100000001100000=966.右移運算:>>運算規(guī)則i>>n把i各位全部向右移動n位最右端的n位被移出丟棄最左端的n位用0補齊(邏輯右移)或最左端的n位用符號位補齊(算術右移)用法右移n位相當于除以2n,并舍去小數部分運算速度比真正的除法和冪運算快得多例
以下程序的運行結果是
。#include<stdio.h>voidmain(){
inta=9,b=-9;
printf("%d,%d",a>>2,b>>2);}結果:2,-3(-9的補碼:1111111111110111,右移后為1111111111111101)應用示例①從整數a最右端第m個位置開始取該位開始右面n位。算法如下:
b=a>>(m-n+1)c=~(~0<<n)d=b&c
注:位自右向左從0開始編號應用示例②將一個整數a循環(huán)右移n位。算法如下:b=a<<(16-n)c=a>>nc=c|b
【例9.7】取一整數從右端開始的4~7位?!汲绦蚍治觥剑?)先將這個整數的4~7位右移到0~3位,這很容易用右移運算來實現。(2)再用一個0~3位為1其余位為0的數(15)來與整數進行“與”運算?!芭c”運算的結果就是這個整數的4~7位。#include<stdio.h>voidmain(){unsigneda,b,d;
scanf("%o",&a);b=a>>4;d=b&15;
printf("%o,%d\n%o,%d\n",a,a,d,d);}
若輸入八進制數321,輸出結果是什么?練習:將16進制短整數按二進制打印輸出.
輸入:F1E2
輸出:1111000111100010
輸入:13A5
輸出:0001001110100101算法思想:從高位到低位逐位測試每一位是0或是1??身槾卧O置屏蔽字分別為100000000000
0000、010000000000
0000、……、00000000
00000001,與該數進行&運算,從而保留所需的一個位的狀態(tài)(其余各位為0)。若結果非零則輸出1,否則輸出0。#include<stdio.h>voidmain(){
inti; shorta;
scanf("%X",&a); for(i=15;i>=0;i--) printf("%1d",a&1<<i?1:0);}
字節(jié)是存儲數據的最小單位,而實際上有些信息是不需要一個字節(jié)存儲的,如邏輯值“真”或“假”只需要1位即可。在計算機用于過程控制、參數檢測或數據通信領域時,控制信息往往只占一個字節(jié)中的一個或幾個二進制位,常常在一個字節(jié)中放幾個信息。9.4.2位段怎樣向一個字節(jié)中的一個或幾個二進制位賦值和改變它的值呢?問題:在C語言中,可以用兩種方法實現位存儲。
1.人為實現人為地將一個字節(jié)data設成幾項。例如:a、b、c、d分別占1、1、4、2位;如果想將c的值從原來的0變?yōu)?2,可以進行如下操作:(1)將數12左移4位,使1100成為右面起第4~7位。(2)將data與“12<<4”進行“按位或”運算,即可將c的值變成12。如果c的原值不為0,要先將c變成0,然后再進行(1)、(2)步的運算。讓c的值變成0的實現方法是data=data&15;這就使data第4~7位為0(即c的值為0),其余各位不變。將上面的幾個步驟結合起來,可得到改變data中c值的方法:data=data&15|(n&15)<<4;其中,n為將賦給c的值,(n&15)的作用是只取n的右端4位的值,其余各位置0,即把n放到最后4位上,(n&15)<<4將n置在4~7位上。此方法使用起來相對難以理解一些,下面介紹一種更為方便的方法----位段來處理這個問題。2.位段(1)位段的概念:C語言允許在一個結構體中以位為單位來指定其成員所占內存長度,這種以位為單位的成員稱為“位段”或稱“位域”(bitfield)。(2)位段的定義:在結構體成員變量的定義后面加上冒號和位數。定義格式為struct
位段結構體名{unsigned成員1:位數;…unsigned
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 系統(tǒng)分析師考試秘訣試題及答案集
- 2025年軟件評測師考試趨勢分析試題及答案
- 農行考試題目及答案
- 中級社會工作者對話技術實操試題及答案
- 數學早中晚試題及答案
- 網絡規(guī)劃客戶滿意度的提升策略試題及答案
- 系統(tǒng)集成考試重點試題及答案
- 報考須知系統(tǒng)集成試題及答案
- 2025年軟件評測師考試全面?zhèn)淇疾呗栽囶}及答案
- 二級信息管理考試內容概述試題及答案
- 埋石混凝土單元工程評定表
- 寄生蟲糞便檢查法演示文稿
- GB/T 7984-2001輸送帶具有橡膠或塑料覆蓋層的普通用途織物芯輸送帶
- 四川省普通高中學生借讀申請表
- 馬克思主義人的自由全面發(fā)展理論
- 信息披露申請表(買家)
- 少年宮信息技術興趣小組活動記錄
- 燃煤電廠鍋爐煙氣靜電除塵裝置設計
- 4008S血液透析機 簡易操作說明書
- 繩索救援技術訓練科目
- 火焰探測器紅外火焰探測器·紫外火焰探測器
評論
0/150
提交評論