官术网_书友最值得收藏!

5.1 認識常量

5.1.1 字面常量

字面常量也許是在程序中使用得最多的常量了,例如,直接出現的各種進制的數字、字符(''括住的單個字符)或字符串(""括住的一系列字符)等。實際上,只存在基本數據類型的字面常量。示例5-1是一些字面常量的例子。

示例5-1

            x = -100.25f;
            #define OPEN_SUCCESS  0x00000001
            char c = 'a';
            char*pChar="abcdef";   //取字符串常量的地址
            int *pInt = NULL;

由于字面常量只能引用,不能修改,所以語言實現一般把它保存在程序的符號表里而不是一般的數據區中。符號表是“只讀”的,其實它是一種訪問保護機制,千萬不要理解為只讀存儲器(ROM)。除了字符串外,你無法取一個字面常量的地址,例如,int *p = &5;這類語句是錯誤的。當你試圖通過常量字符串的地址修改其中的字符時就會報告“只讀”錯誤。例如:

                *(pChar+2)='k';  // 錯誤,不能修改字面常量的內存單元

如果程序中到處都充斥著各種各樣的字面常量,那么就可能存在相同的常量,我們應該把相同的常量合并,以減少內存消耗。有些連接器自動執行常量合并,而有的連接器則提供了常量合并的開關,以優化程序的效率。如果你的編譯鏈接環境支持常量合并,那么請將它打開。

5.1.2 符號常量

存在兩種符號常量:用#define定義的宏常量和用const定義的常量。由于#define是預編譯偽指令,它定義的宏常量在進入編譯階段前就已經被替換為所代表的字面常量了,因此宏常量在本質上是字面常量。在C語言中,用const定義的常量其實是值不能修改的變量,因此會給它分配存儲空間(外連接的)。但是在C++中,const定義的常量要具體情況具體對待:對于基本數據類型的常量,編譯器會把它放到符號表中而不分配存儲空間,而ADT/UDT的const對象則需要分配存儲空間(大對象)。還有一些情況下也需要分配存儲空間,例如,強制聲明為extern的符號常量或取符號常量的地址等操作,都將強迫編譯器為這些常量分配存儲空間,以滿足用戶的要求。

你可以取一個const符號常量的地址:對于基本數據類型的const常量,編譯器會重新在內存中創建它的一個拷貝,你通過其地址訪問到的就是這個拷貝而非原始的符號常量;而對于構造類型的const常量,實際上它是編譯時不允許修改的變量,因此如果你能繞過編譯器的靜態類型安全檢查機制,就可以在運行時修改其內存單元,見示例5-2。

示例5-2

            const long lng = 10;
            long*pl=(long*)&lng;          // 取常量的地址
            *pl=1000;                     // “迂回修改”
            cout<<*pl<<endl;              //1000,修改的是拷貝內容!
            cout<<lng<<endl;              //10,原始常量并沒有變!
            class Integer {
            public:
              Integer() : m_lng(100) { }
              long m_lng;
            }
            const Integer int_1;
            Integer*pInt=(Integer*)&int_1;// 去除常數屬性
            pInt->m_lng = 1000;
            cout<<pInt->m_lng<<endl;      //1000,修改const對象
            cout<<int_1.m_lng<<endl;      //1000,“迂回修改”成功

這個例子說明:const符號只是編譯時(源代碼級或語言層面)強類型安全檢查機制的一種手段,以幫助程序員發現無意中要修改它們的代碼并進行糾正,而在運行時(二進制層面)無法阻止惡意的修改。也可以說就是“防君子不防小人”。

【提示5-1】: 從理論上講,只要你手中握有一個對象的指針(內存地址),你就可以設法繞過編譯器隨意修改它的內容,除非該內存受到操作系統的保護。也就是說,C++并沒有提供對指針有效性的靜態檢查,而是把它丟給了操作系統,這正是使用指針的危險所在。

【提示5-2】: 在標準C語言中,const符號常量默認是外連接的(分配存儲),也就是說你不能在兩個(或兩個以上)編譯單元中同時定義一個同名的const符號常量(重復定義錯誤),或者把一個const符號常量定義放在一個頭文件中而在多個編譯單元中同時包含該頭文件。但是在標準C++中,const符號常量默認是內連接的,因此可以定義在頭文件中。當在不同的編譯單元中同時包含該頭文件時,編譯器認為它們是不同的符號常量,因此每個編譯單元獨立編譯時會分別為它們分配存儲空間,而在連接時進行常量合并。

5.1.3 契約性常量

契約性const對象的定義并未使用const關鍵字,但被看作是一個const對象,見示例5-3。

示例5-3

            void ReadValue(const int& num)
            {
              cout << num;
            }
            int main(void)
            {
              int n = 0;
              ReadValue(n);  // 契約性const,n被看作是const
            }

5.1.4 枚舉常量

C++/C的構造類型enum實際上常用來定義一些相關常量的集合。標準C++/C規定枚舉常量的值是可以擴展的,并非受限于一般的整型數的范圍(見示例5-4)。

示例5-4

            enum Gigantic
            {
              SMALL = 10,
              GIGANTIC = 300000000000
            };

至于底層如何實現,則依賴于具體的環境和編譯器廠商,可能會有不同的語義,請查看編譯器文檔。

主站蜘蛛池模板: 深水埗区| 景洪市| 汨罗市| 沙雅县| 永宁县| 伊通| 布拖县| 海口市| 隆安县| 吴川市| 玉屏| 杭州市| 德阳市| 石城县| 石河子市| 浠水县| 台湾省| 江永县| 遵义市| 舟山市| 射洪县| 望奎县| 许昌市| 嵊州市| 花莲市| 灌云县| 闵行区| 遂川县| 昌都县| 托克逊县| 萝北县| 耒阳市| 昌黎县| 文登市| 唐山市| 永德县| 康定县| 无极县| 大田县| 旬阳县| 中牟县|