字串常量
我相信,使用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的語義,任何想要破壞這個語義的事情都會給我們帶來未知的結果。
相關文章
- 字串常量池字串
- c指標之字串常量指標字串
- 徹底搞清楚class常量池、執行時常量池、字串常量池字串
- java中字串常量池的用法Java字串
- C語言進階[2]:字串常量C語言字串
- Java常量池解析與字串intern簡介Java字串
- java基礎:String — 字串常量池與intern(二)Java字串
- 正確理解和使用JAVA中的字串常量池Java字串
- JAVA虛擬機器-方法區與字串常量池Java虛擬機字串
- 檢測一個字串是否在jvm的常量池中字串JVM
- 字串指標與字元陣列的區別--字串常量的值不能改字串指標字元陣列
- 字串常量到底存放在哪個儲存區字串
- 重構遺留程式碼(2):魔術字串和常量字串
- 深入探究JVM之記憶體結構及字串常量池JVM記憶體字串
- Python進階:如何將字串常量轉化為變數?Python字串變數
- 一頁解決字串常量池相關疑難雜症字串
- Java String類,字串常量池,建立方法,字串的獲取,擷取,轉換,分割。Java字串
- 從字串到常量池,一文看懂String類設計字串
- 常量
- 在Java虛擬機器中,字串常量到底存放在哪Java虛擬機字串
- 指標常量和常量指標指標
- 使用Microsoft Roslyn提取C#和VB.NET原始碼中的字串常量ROSC#原始碼字串
- 執行時常量池的再深入,從jvm的記憶體分配角度談談這道字串常量池的面試題。JVM記憶體字串面試題
- Struts2中 用OGNL表示式定義字串常量 與單個字元常量需要注意的一個小細節 (轉)...字串字元
- 指標常量和常量指標的區別指標
- 詳解 常量指標和指標常量指標
- SciPy 常量模組
- Go之旅-常量Go
- 面試話癆(四)常量在哪裡呀,常量在哪裡面試
- 指向常量資料的指標和常量指標指標
- 好好說說Java中的常量池之Class常量池Java
- Swift 常量講解Swift
- go 語言常量Go
- 常量與變數變數
- java的常量池Java
- php 中的常量PHP
- 指標和常量指標
- 再學C/C++ 之 指標常量 和 常量指標C++指標