(2)Enumeration constant(列舉常數)
在C語言中,當要定義一個常數時,可以利用巨集或列舉,如下:
#define MAX 10
or
enum { MAX = 10 }; //unnamed enumeration(無名列舉)
在C++中,當要定義一個常數時,可以利用C語言的方法,而C++中也有提供另一個方法
#define MAX 10
or
enum { MAX = 10 };
or
int const MAX = 10; or const int MAX = 10; //const objects
為什麼不直接在程式中直接寫常數在所需要的地方呢?
有二個常見的原因
•減少程式碼的修改:假設程式中有一百個地方要從原本的10改為20,如果真得一個一個改
改到手的抽筋了!!當然,也可以直接利用編譯器提供的取代功能來使用。
但寫的人敢保證,不會取代到其它的數值嗎?(像100或1000之類的)
如果使用以上任何一種宣告常數的方法,只需要更改常數宣告的地方即可。
•數字意思文字化:在程式突然出現一個10或27,誰知道是什麼意思呢?如果10是表示陣列的大小,
這樣寫可能會比較讓別人瞭解 #define ARRAY_SIZE 10 對吧!
那利用define、enum與const有什麼差別呢?基本只談論差別不談論優劣
(a)define
說到define就要先談起何謂Preprocessor(前置處理器),說到Preprocessor就要談起一個source file
是編譯的,過程是如此的:
C/C++ source file ==> Preprocessor ==> Compiler
Preprocessor是根據source file中的Preprocessor directive(前置處理指令;前置指令)來執行各種動作,再將
執行之後的source file交由Compiler進行compile的動作。Preprocessor directive的指令如下:
#define #undefine #include #if #ifdef #ifndef #else #elif #endif #line #error #pragma
#define的語法是如此的(一般都以大寫來表示,但沒有規定一定要大寫)
#define DEFINE_SYMBOL SYMBOL_CONTENT
DEFINE_SYMBOL : 定義一個名稱代表SYMBOL_CONTENT的值或表示式
SYMBOL_CONTENT:定義DEFINE_SYMBOL所要表示的值或表示式,可以沒有SYMBOL_CONTENT
#include<stdio.h>
#include<math.h>
#define PI 4*atan(1)
#define SIN(X) sin((X)*PI/180)
void main()
{
printf("%f\n",SIN(45));
}
以上程式經過preprocessor處理之後的程式如下:
void main()
{
printf("%d\n",sin((45)*4*atan(1)/180));
}
做的動作是"取代"。這就是利用#define來產生常數的資料方式。
(b)enum
在前一篇文章介紹過,enum在編譯期間(at compile-time)已經建立起,其列舉元會被視為常數。
但請注意之前說過列舉元設定的值必須是常數值或另一個列舉元,所以無法像這樣:enum { PI = 4 * atan(1) };
舉二個應用enum的例子:
(Ⅰ)用來class中:
class Stack
{
private:
enum { SIZE = 20 };
int stack[SIZE];
int top;
public:
int push(const int& element) ;
int pop() const;
...
}
(Ⅱ)
enum DIM_SIZE { ROW = 20 , COLUMN = 30 };
void main()
{
int iArray[ROW][COLUMN];
...
...
}
(c)const
const必須一開始就被初始化,若沒有初始化時,編譯器會產生錯誤的訊息,告知const物件必須初始化
接著來看const的產生方式,以下的結果是在Release build時所產生的ASM碼,不是Debug build時所產生的ASM
碼,Release build與Debug build有什麼差別呢?差別在於
Debug build會產生全部變數、表示式、...等的Debug資訊(含ASM碼、...),並且不做optimization(優化)
而Release build並不會產生Debug所需要的information,並且會做optimization的動作,讓程式的效率更好更小
一般來說,要觀看C/C++程式碼真正編譯之後的動作,是看Release build之後的ASM碼。
在要如何在Release build下Debug呢?需要做一些設定,參考這一篇文章:Debugging a Release Build
接下來正式來看以下這段程式碼:
const int *pctr=0;
const int num = 20;
pctr = #
cout<<*pctr;
00401000 push 14h
00401002 mov ecx,offset std::cout (00427318)
00401007 call std::basic_ostream<char,std::char_traits<char> >::operator<< (00401010)
所產生的ASM碼,其實只有最後一行cout,其餘都已在compile-time的時候被compiler給compile掉了!
編譯器知道常數num是20,而pctr指向它,所以*pctr就直接取代成20,這是一個很smart compiler!
常數的性質在編譯期間就已被處理完畢,所以在run-time時是不佔有位置的。
const int *pctr=0;
00401554 mov dword ptr [pctr],0
const int num = 20;
0040155B mov dword ptr [num],14h
pctr = #
00401562 lea eax,[num]
00401565 mov dword ptr [pctr],eax
cout<<*pctr;
00401568 mov ecx,dword ptr [pctr]
0040156B mov edx,dword ptr [ecx]
0040156D push edx
0040156E mov ecx,offset std::cout (00454828)
00401573 call @ILT+245(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004010fa)
會發現每一行都會產生相對應的ASM碼,這是因為Debug build必須加上debug所需要資訊及沒有做優化的動作。
continue...
Written By James On 2004/02/08