三、C#的預處理
預處理階段是一個(gè)文本到文本的轉換階段,在預處理過(guò)程中,使能進(jìn)行代碼的條件包含和排除。
pp-un it:
pp-gro up opt
pp-gro up:
pp-gro up-part
pp-gro up pp-group-part
pp-gro up-part:
pp-tokensopt new-line
pp-de claration
pp-if -section
pp-con trol-line
pp-l ine-number
pp-tokens:
pp-token
pp-tokens pp-token
pp-token:
identifi er
keyword
literal
operator-or-punctuator
new-line:
The carriage return character (U+000D)
The line feed character (U+000A)
The carriage return character followed by a line feed character
The line separator character (U+2028)
The paragraph separator character (U+2029)
1、預處理聲明
在預處理過(guò)程中,為了
使用名稱(chēng)可以被定義和取消定義。#define 定義一個(gè)標識符。#undef “反定義”一個(gè)標識符,如果一個(gè)標識符在以前已經(jīng)被定義了,那么它就變成了不明確的。如果一個(gè)標識符已經(jīng)被定義了,它的語(yǔ)意就等同于true ;如果一個(gè)標識符沒(méi)有意義,那么它的語(yǔ)意等同于false。
pp-de claration:
#define pp-identifier
#undef pp-identifier
來(lái)看看這個(gè)例子:
#define A
#undef B
class C
{
#if A
void F()
#else
void G()
#endif
#if B
void H()
#else
void I()
#endif
}
變?yōu)?
class C
{
void F()
void I()
}
如果有一個(gè)pp-unit, 聲明就必須用pp- token 元素進(jìn)行。換句話(huà)說(shuō),#define 和#undef 必須在文件中任何 “真正代碼”前聲明,否則在編譯時(shí)會(huì )發(fā)生錯誤。因此,也許會(huì )像下面的例子一樣散布#if 和#define:
#define A
#if A
#define B
#endif
namespace N
{
#if B
class Class1
#endif
}
因為#define 放在了真實(shí)代碼后面,所以下面的例子是非法的:
#define A
namespace N
{
#define B
#if B
class Class1
#endif
}
一個(gè)#undef 也許會(huì )“反定義”一個(gè)沒(méi)有定義的名稱(chēng)。下面的例子中定義了一個(gè)名字并且對它進(jìn)行了兩次反定義,第二個(gè)#undef 沒(méi)有效果,但還是合法的。
#define A
#undef A
#undef A
2、 #if, #elif, #else, #endif
pp-if -section 用來(lái)對程序文本的一部件進(jìn)行有條件地包括和排除。
pp-if -section:
pp-if -group pp-elif-groupsopt pp-else-groupopt pp-endif-line
pp-if -group:
#if pp-expression new-line pp -group opt
pp-e lif -groups
pp-e lif -group
pp-e lif -groups pp-elif-group
pp-e lif -group:
#elif pp-expression new-line group opt
pp-e lse-group:
#else new-line group opt
pp-end if -line
#endif new-line
舉個(gè)例子:
#define Debug
class Class1
{
#if Debug
void Trace(string s)
#endif
}
變成:
class Class1
{
void Trace(string s)
}
如果這部分可以嵌套。
來(lái)看看例子:
#define Debug // Debugging on
#undef Trace // Tracing off
class PurchaseTransaction
{
void Commit() {
#if Debug
CheckConsistency();
#if Trace
WriteToLog(this.ToString());
#endif
#endif
CommitHelper();
}
}
3、預處理控制行
特性#error和#warning使得代碼可以把警告和錯誤的條件報告給編譯程序,來(lái)查出標準的編譯時(shí)的警告和錯誤。
pp-con trol-line:
#error pp-message
#warning pp-message
pp-message:
pp-tokensopt
舉個(gè)例子幫助大家理解
#warning Code review needed before check-in
#define DEBUG
#if DEBUG && RETAIL
#error A build can't be both debug and retail!
#endif
class Class1
{…}
這將總是產(chǎn)生警告(“Code review needed before check-in"),并且如果予處理修飾符DEBUG 和RETAIL 都被定義,還會(huì )產(chǎn)生錯誤。
4、 #line
#line 的特點(diǎn)使得開(kāi)發(fā)者可以改變行的數量和編譯器輸出時(shí)
使用的源文件名稱(chēng),例如警告和錯誤。如果沒(méi)有行指示符,那么行的數量和文件名稱(chēng)就會(huì )自動(dòng)由編譯器定義。#line指示符通常用于編程后的工具,它從其它文本輸入產(chǎn)生C#源代碼。
pp-l ine-number:
#line integer-literal
#line integer-literal string-literal
pp-in teger-literal:
decimal-digit
decimal-digits decimal-digit
pp-s tring-literal:
" pp-string-literal-characters "
pp -string-literal-characters:
pp-s tring-literal-character
pp-s tring-literal-characters pp-string-literal-character
pp-s tring-literal-character:
Any character except " (U+0022), and white-space
5、預處理標識符
預處理標識符
使用和規則C#標識符文法相似的文法:
pp -identifi er:
pp-ava ilable-identifier
pp-ava ilable-identifi er:
A pp-identif ier-or-keyword that is not true or false
pp-id entif ier-or-keyword:
identifi er-start-character identif ier-part-characters opt
true 和false 符號不是合法的預定義指示符,所以不能用于#define 的定義和#undef 的反定義。
6、預處理表達式
操作符!, ==, !=, && 和||是允許的預定義表達式。在預定義表達式中,圓括號可以用來(lái)分組。
pp-expression:
pp-equality-expression
pp-pr imary-expression:
true
false
pp -identifi er
( pp-expression )
pp-unary-expression:
pp-pr imary-expression
! pp-unary-expression
pp-equality-expression:
pp-equality-expression == pp-logical-and-expression
pp-equality-expression != pp-logical-and-expression
pp-logical-and-expression:
pp-unary-expression
pp-logical-and-expression && pp-unary-expression
pp-logical-or-expression:
pp-logical-and-expression
pp-logical-or-expression || pp-logical-and-expression
7、與空白交互作用
條件編譯標識符必須在一行的第一個(gè)非空白位置。
一個(gè)單行注釋可以跟在條件編譯指示符或pp-c ontrol-line 標識符后面。
例如:
#define Debug // Defined if the build is a debug build
對于pp-control-line 標識符,一行的剩余組成pp-message,獨立于此行的注釋。
看看例子:
#warning // TODO: Add a better warning
會(huì )有一個(gè)注釋為"http:// TODO: Add a better warning"的警告。
一個(gè)多行注釋的起始和結束可以不在同一行中,就像條件編譯標識符。
就像這個(gè)例子:
/* This comment is illegal because it
ends on the same line*/ #define Debug
/* This is comment is illegal because it is on the same line */ #define
Retail
#define A /* This is comment is illegal because it is on the same line */
#define B /* This comment is illegal because it starts
on the same line */
結果將是編譯時(shí)錯誤。
可以形成一個(gè)條件編譯標識符的數據符號可能會(huì )隱含在注釋中。
看看這個(gè)例子:
// This entire line is a commment. #define Debug
/* This text would be a cc directive but it is commented out:
#define Retail
*/
不包含任何條件編譯標識符,然而完全由空白組成。
小知識:編譯語(yǔ)言與解釋語(yǔ)言的優(yōu)缺點(diǎn)對比
1、運行效率:
編譯語(yǔ)言需要編譯一次,運行直接執行、不需要翻譯,所以編譯型語(yǔ)言的程序執行效率高。而解釋語(yǔ)言則不同,解釋型語(yǔ)言的程序不需要編譯,省了道工序,解釋性語(yǔ)言在運行程序的時(shí)候才翻譯,比如解釋型basic語(yǔ)言,專(zhuān)門(mén)有一個(gè)解釋器能夠直接執行basic程序,每個(gè)語(yǔ)句都是執行的時(shí)候才翻譯。這樣解釋性語(yǔ)言每執行一次就要翻譯一次,效率比較低。
解釋執行的語(yǔ)言因為解釋器不需要直接同機器碼打交道所以實(shí)現起來(lái)較為簡(jiǎn)單、而且便于在不同的平臺上面移植,這一點(diǎn)從現在的編程語(yǔ)言解釋執行的居多就能看出來(lái),如 Visual Basic、Visual Foxpro、Power Builder、Java...等。編譯執行的語(yǔ)言因為要直接同CPU 的指令集打交道,具有很強的指令依賴(lài)性和系統依賴(lài)性,但編譯后的程序執行效率要比解釋語(yǔ)言要高的多,象現在的 Visual C/C++、Delphi 等都是很好的編譯語(yǔ)言。
2、代碼
安全性
對于解釋語(yǔ)言與編譯語(yǔ)言所編制出來(lái)的代碼
安全性上而言,可以說(shuō)是各有優(yōu)缺點(diǎn)。曾經(jīng)在 Windows 下跟蹤調式過(guò) VB 程序的朋友一般都知道,程序代碼 99% 的時(shí)間里都是在 VBRUNxx 里轉來(lái)轉去,根本看不出一個(gè)所以然來(lái)。 這是因為你跟蹤的是 VB 的解釋器,要從解釋器中看出代碼的目的是什么是相當困難的。但解釋語(yǔ)言有一個(gè)致命的弱點(diǎn),那就是解釋語(yǔ)言的程序代碼都是以偽碼的方式存放的,一旦被人找到了偽碼與源碼之間的對應關(guān)系,就很容易做出一個(gè)反編譯器出來(lái),你的源程序等于被公開(kāi)了一樣。而編譯語(yǔ)言因為直接把用戶(hù)程序 編譯成機器碼,再經(jīng)過(guò)優(yōu)化程序的優(yōu)化,很難從程序返回到你的源程序的狀態(tài), 但對于熟悉匯編語(yǔ)言的解密者來(lái)說(shuō),也很容易通過(guò)跟蹤你的代碼來(lái)確定某些代碼的用途。
然而,我們會(huì )看到在對象上完成運算的方法有所不同。
object.member_function(arglist)是對一個(gè)對象“調用一個(gè)成員函數”。而在面向對象的用法中,也稱(chēng)之為“向一個(gè)對象發(fā)送消息”。這樣,對于stash K,語(yǔ)句K.add(&i)“發(fā)送消息給K”,也就是說(shuō),“對自己add( )”。事實(shí)上,面向對象程序設計可以總結為一句話(huà),“向對象發(fā)送消息”。需要做的所有事情就是創(chuàng )建一束對象并且給它們發(fā)送消息。當然,問(wèn)題是勾畫(huà)出我們的對象和消息是什么,但如果完成了這些,C++ 的實(shí)現就直截了當了。
一個(gè)結構的大小是它的所有成員大小的和。有時(shí),當一個(gè)struct 被編譯器處理時(shí),會(huì )增加額外的字節以使得捆綁更整齊,這主要是為了提高執行效率。
另外,用sizeof可以計算出struct的內存大小。
下面我們來(lái)看看相應的例子:
//計算struct內存
#include <stdio.h>
struct A1
{
int i[100];
};
struct A2
{
void x();
};
void main(void)
{
printf(“size of struct A is %d\n”, sizeof(A1);
printf(“size of struct B is %d\n”, sizeof(A2);
}
第一句printf會(huì )打印出200,而第二句則會(huì )打印出一個(gè)不確定的非零值。
因為這個(gè)聲明這個(gè)在C中不合法,但是在C++中是合法的。
你可以拿這個(gè)對比一下C#的聲明??纯从行┦裁床煌??