傳承歷史,包容遺憾(轉)

使用 C/C++ 多年的人對下面這個字符串賦值語句都不會陌生吧。

 

              char* p = "test";

 

同時,我也相信,各位在使用這種語句後吃過很多苦頭也不少吧?只要你想利用指針 p 來改變字符串的內容,你的程序都會得到一個讓你顏面盡失一個內存非法操作。比如,下面的這些語句:

 

              p[0] = 's';

              strcpy(p, "haoel");

 

原因就在於, char* p = "test"; 這個聲明,聲明瞭一個指針,而這個指針指向的是全局的 const 內存區 const 內存區當然不會讓你想改就改的。所以,如果你一定要寫這塊內存的話,那就是一個非常嚴重的內存錯誤。另,之所以加粗 全局 const 內存區 ,是強調一下,如果你不信的話,你可以試試下面這段代碼,看看 p1 p2 的地址是不是一樣的。

 

              char* p1 = "anything";

              char* p2 = "anything";

              printf(“ p1=%x, p2=%x /n”, p1, p2);

 

我想這應該是一個衆所周知的問題吧。取而代之的,應該是使用數組來做初始化聲明。如: char str[] = “hello world”; 如果現在還有哪本書中的 C 的示例採用了使用 const 字符串初始化指針的這種方式,那麼你就可以把那本書撕了,如果這本書是 C++ 的書話,那麼你應該把這個作者和這個出版社告上法庭,因爲你不應該容忍這種學術騙子。如果你的部門的開發人員還有人寫出這種代碼的話,如果他是 C 程序員,我想你可以在打過他的屁股後告訴他下不爲例,如果他是一個 C++ 程序員的話,我想你可以懷疑他是否有資格做一個 C++ 程序員了。

 

       至於你問我爲什麼要對學 C++ 的人那麼苛刻,那是因爲學過 C++ 的人都知道 C++ 中的 const 關鍵字的有着什麼樣的權力,你也應該知道 C++ const 有着無比的照顧和關愛,幾乎所有關於 C++ 的書都會提到 const 這東西。所以,如果作爲一個 C++ 的程序員來說,如果你不知道的話,那就太說不過去了。

 

       我們知道,雙引號引起來的字符串是 const 的,所以,在 C++ 的世界中,你應該進行如下的聲明才比較穩妥:

             

              const char *p = "test";

 

這樣,當你修改這個字符串的內容時,編譯器會給你一個錯誤而導致你的程序編譯不通過,從而不會產生運行時的內存錯誤。

 

       可問題是,像 C++ 這種對類型要求很嚴格的語言來說,爲什麼它在編譯諸如 char *p="test" 程序的時候不出錯,甚至連個警告都沒有( g++ vc++7 )?難道這是他的一個 bug ?我想,這應該是對古老的 C 的一個向下兼容。因爲,在 C 的世界中,這種用法太多了。

 

C++ 中,比如:函數的參數和異常的捕獲都存在這種問題,如下所示:(因編譯器而定,在 gcc 3.4.3 版中,下例中的異常示例不能被捕獲,但 VC++6 中卻可以被捕獲)

 

       func( char* p) { }   // 以這種方式調用函數 func(“abc”);

      

       try { thow “exception”; } catch (char* p) { }

 

       這些都是 C++ 編譯器默認了可以把 const char* 轉成 char* 的罪行,無疑會對大家是一個誤導。甚至讓人無所畏懼地走入其中,並自以爲走入了正途。這樣看來,這種向下兼容的 C++ 標準,就顯得有點誤人不淺了

 

       不過好在, C++ 標準委員會早已意識到了這一點。這個 C++ feature 被定義爲了 “Deprecated Feature” ,即 不被建議使用的特性 。意思就是,在將來,這種特性將被從 C++ 中移出,於是,你目前的這種程序將無法在新的 C++ 編譯器上編譯通過。對於程序的可移植性來說,我們今天所寫的代碼尤其要注意這些 “Deprecated Feature”

 

       據我所知,目前 C++ 中被列爲 “Deprecated Feature” 如下所示(可能不準確,請大家指正)下面的這些 feature 都已被 C++ 標準委員會訂爲廢除 featrue 了。

 

一、            隱晦的字符串的 const 轉換。

char *p = "test";

w_char *pw = L"test";

       把一個 const 的字符串類型轉成 non-const 的。包括指針和數組。

 

二、            隱晦的類型聲明。

func() {}   // 函數的隱晦返回類型是 int

static num;    // 變量的隱晦類型是 int

       這種 feature C89 中還可以使用,但在 C99 C++ 中都被去除了。( gcc 3.4 版本對於這種聲明會給出編譯錯誤,而 VC++6.0 會認爲這是合法的程序)

 

三、            布爾變量的累加操作。

bool isConn = false;

isConn++;          // 這個操作會把 isConn 變爲 true

就目前而言,幾乎所有的編譯器都認可這種操作,但這種用法也是不被建議的,終有一天會被取消。

 

四、            更改父類成員的存取權限。

 

class B

{

    protected:

        int i;

};

 

class D : public B

{

     public:

        B::i; // 這種方式可能大家很少看到。

};

 

       對於這種語法,子類重新暴露了父類的私有成員。這會帶來很大的安全性問題。目前而言,這個 feature 對於所有的編譯器來說應該都是可以編譯通過的(連個 Warning 都沒有)。但這個 feature 也是要被廢除的。

 

五、            文件中域的 static 聲明

static int i;

static void func()

 

       據說,這種舊的在 C 中的爲了實現其作用域在本文件中的 feature 在未來的 C++ 中也要被取消。

 

 

文章到這裏應該結束了,在結束之前,讓我再給大家共享一個有趣的關於 const 的例子(在網上看到的)

    const int a = 1;

    int *p = const_cast<int*>(&a);

    *p = 2;

    cout << “value a=”<< a << endl;

    cout << “value *p=” <<*p << endl;

    cout << “address a=” <<&a << endl;

    cout << “address p=” <<p << endl;

這段代碼輸出的結果如下:

 

value a=1

value *p=2

address a=0xbff1d48c

address p=0xbff1d48c

 

地址都是一樣的,可值爲什麼不一樣呢?呵呵。這個問題看起來有點 學術味 過濃,不過是個好例子,可以讓你知道 C++ 的一些用法和一些原理。有以下幾個方面大家可以考慮一下:

1 const int a = 1 是不是和宏有點像,會不會被編譯器優化了?

2 )去修改一個 const 的值,本來應該是不對的。這可能會是向舊的 C 兼容。是否會讓編譯器產生未知行爲?

  所以,這個示例也告訴我們,我們應該遵循 C++ 中的 const non-const 的語義,任何想要破壞這個語義的事情都會給我們帶來未知的結果。

發佈了20 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章