字符串常量初始化指針

  • 2019 年 10 月 3 日
  • 筆記

今天寫個小文說一說字符串地址和字符串常量。

在C/C++中,一個字符串常量表示的是該字符串第一個元素的地址,就跟char指針名,char數組名表示的是字符串第一個元素的地址一樣。

想要打印一個地址,用一個簡單的 cout << 地址; 語句就可以搞定;

但是下面這兩條語句將打印整個字符串

char a[20] = "1234";  cout << a << endl;    char *p = a;  cout << p << endl;

這也是字符數組 與其他數組不同的一個地方,那麼該如何得到該字符串的地址呢?

下面有兩種方法可供參考

cout << (int*)a << endl;  cout << &a << endl;

這兩種方法都可以正確打印出“字符串的地址”,但是有細微區別之處

在字符數組a中,a表示第一個字符的地址,a+1表示第二個字符的地址;

在第一條打印地址的語句中,(int*)a只是起到了一個強制類型轉換的作用,換句話說,a表示第一個字符的地址,但是cout <<a;輸出的是整個字符串,這是因為這個地址是char*類型的,cout識別到char*類型的地址將會自動打印從該地址指向的空間開始直到遇到’’的字符串內容,所以這裡我們只需要進行一個強制類型轉換(這裡舉例強制轉換為int*,可以轉換為其他類型的指針,只要不是char*,哪怕是double*也可以正確打印地址)。所以如果想要第二個字符的地址呢,cout << (int*)(a+1);就可以啦

第二條打印地址的語句中,直接使用了&a;這裡需要強調的是,不論任何數組,數組名表示的都是第一個元素的地址,&數組名表示的才是該數組的地址,雖然二者打印出來是同一地址,但是如果進行指針運算則天差地別,看一下下面這段代碼和效果

int a[10];  cout << "a  is: " << a << " a+1  is: " << a+1 << endl;  cout << "&a is: " << &a << " &a+1  is: " << &a+1 << endl; 

有圖有真相,a+1隻是向後移動了4個位元組,而&a+1直接向後移動了40個位元組,所以&數組名表示的才是整個數組的地址。所以同理用&字符數組名也可以打印出字符串地址。

 下面說一說字符串常量,首先字符串存儲在靜態存儲區,其次,不可修改。

於是就產生了下面幾條語句

char a[20] = "1234";  char * p = "1234";  const char * p = "1234";

第一條語句不用說,很常用了,正確,那麼二三條語句哪個對哪個錯呢。

—————————-以下為9.7號加更—————————-

很明顯,像”1234″這樣的字符串常量被存儲在靜態存儲區中,他們的類型是const char*型,根據類型匹配原則,很顯然將其賦給一個char * 的指針肯定是不行的。第三條語句百分百正確。

但是第二條語句在c 和 c++兩種環境下編譯產生了微小差異。

在C環境下編譯程序正常運行,且0warning.

而在C++下,則產生了一條警告

 

 這是因為在傳統的原始C中,是沒有const的概念的,也就是說在C中沒有硬性規定常量”1234″不能修改,但是再進一步嘗試着修改時,會產生內存錯誤,也就是說字符串常量”1234″是const char*類型的,不能被修改

而在C++中規範了這種情況,明確規定”常量1234″就存儲於靜態存儲區,這裡沒有報錯而只是拋出一條警告僅僅是為了兼容C。

所以不論是在C還是C++中建議使用const char*指向一個字符串常量。

——————————end——————————————-

剛開始我還覺得加不加const無所謂,反正都是錯的,後來細思極恐,果然還是知識貧窮限制了想像力;

一開始我認為後面兩句錯的原因是因為 沒見過,看着像是給*p賦了個”1234″,可又不是,這是個什麼東西,總之就是看着怪怪的;

所以這裡就牽扯到開頭提到的一個知識點了;在C/C++中,一個字符串常量表示的是該字符串第一個元素的地址,就跟char指針名,char數組名表示的是字符串第一個元素的地址一樣

所以這回就明白了,看着是”1234″,其實本質上是”1234″第一個字符的地址,那麼將 char*型的指針p指向“1234”就沒有問題啦,地址對地址嘛,但是編譯後出錯了,理由是”1234″是一個常量,所以加上了const限定。

關於這裡為什麼要加const限定才正確,如果不加的話類型也是匹配的,為什麼會報錯呢,所以這裡需要再強調一下,“1234”是字符串常量,常量不允許被修改,如果char * p = “1234”;編譯成功,那就意味着”1234″有可能被修改,而這是不被允許的,所以必須乖乖聽話加上const限定符讓系統安心才是嘛。

咦?常量不允許被修改,要加const限定符才可以,那為什麼我們一直用的第一條語句是正確的而且從來沒有報錯過呢?

這就要說說第一條語句和第三條語句的區別啦,“1234”是字符串常量,常量存儲在靜態存儲區,有地址

第三條語句是讓一個常量指針直接指向靜態存儲區的”1234″的地址,因為是直接指向本尊地址,所以要加上const限定符

而第一條語句則是將靜態存儲區的”1234″的副本拿出來複制到字符數組a中,所以這樣初始化可以在後面隨意更改副本”1234″,這樣並不會影響到真正的”1234″;

是真地址假地址cout一下就知道了

1 char a[20] = "1234";  2 const char * p = "1234";  3 cout << &a << endl; // 獲取字符數組a的地址  4 cout << &"1234" << endl; // 獲取 "1234"在靜態存儲區的地址  5  6 //再來驗證一下字符串常量為該字符串第一個元素的地址  7 cout << *"1234" << endl;  8 cout << *("1234"+1) << endl;

 

 

OK,明顯副本”1234“與本尊”1234“的地址不同,而且差了很多很多地址位,肯定是跨內存區了的,最後面兩行想要的結論也得到了驗證,實踐出真知,真是不枉我熬夜到12點。