04. 你不可以試圖用 char* 去更改一個"字串常數"
試圖去更改字串常數(string literal)的結果會是undefined behavior。
錯誤例子:
char* pc = "john"; /* pc 現在指著一個字串常數 */
*pc = 'J'; /* undefined behaviour,結果無法預測*/
pc = "jane"; /* 合法,pc指到在別的位址的另一個字串常數*/
/* 但是"john"這個字串還是存在原來的地方不會消失*/
因為char* pc = "john"這個動作會新增一個內含元素為"john\0"的static char[5],
然後pc會指向這個static char的位址(通常是唯讀)。
若是試圖存取這個static char[],Standard並沒有定義結果為何。
pc = "jane" 這個動作會把 pc 指到另一個沒在用的位址然後新增一個
內含元素為"jane\0"的static char[5]。
可是之前那個字串 "john\n" 還是留在原地沒有消失。
通常編譯器的作法是把字串常數放在一塊read only(.rdata)的區域內,
此區域大小是有限的,所以如果你重複把pc指給不同的字串常數,
是有可能會出問題的。
正確例子:
char pc[] = "john"; /* pc 現在是個合法的陣列,裡面住著字串 john */
/* 也就是 pc[0]='j', pc[1]='o', pc[2]='h',
pc[3]='n', pc[4]='\0' */
*pc = 'J';
pc[2] = 'H';
說明:字串常數的內容應該要是"唯讀"的。您有使用權,但是沒有更改的權利。
若您希望使用可以更改的字串,那您應該將其放在合法空間
錯誤例子:
char *s1 = "Hello, ";
char *s2 = "world!";
/* strcat() 不會另行配置空間,只會將資料附加到 s1 所指唯讀字串的後面,
造成寫入到程式無權碰觸的記憶體空間 */
strcat(s1, s2);
正確例子(2):
/* s1 宣告成陣列,並保留足夠空間存放後續要附加的內容 */
char s1[20] = "Hello, ";
char *s2 = "world!";
/* 因為 strcat() 的返回值等於第一個參數值,所以 s3 就不需要了 */
strcat(s1, s2);
C++對於字串常數的嚴格定義為const char* 或 const char[]。
但是由於要相容C,char* 也是允許的寫法(不建議就是)。
不過,在C++試圖更改字串常數(要先const_cast)一樣是undefined behavior。
const char* pc = "Hello";
char* p = const_cast<char*>(pc);
p[0] = 'M'; // undefined behaviour
備註:
由於不加const容易造成混淆,
建議不管是C還是C++一律用 const char* 定義字串常數。
補充資料:
http://en.cppreference.com/w/c/language/string_literal
http://en.cppreference.com/w/cpp/language/string_literal
字串函數相關:#1IOXeMHX
undefined behavior : 精華區 z -> 3 -> 3 -> 23