c++字元串拷貝和內存問題?
首先我們明確下"abcd"實質是在常量區的一段地址的起始地址
然後我以為 如圖 然後修改 p[2] = 『h『;會報錯修改 a[2] = 『h『;是對的以前我對數組的理解是先有地址 然後把數一個個複製進去 傳值拷貝
但是今天看到 char a[]= "abcd";//其實都是傳遞的地址感覺其實和指針的指向完完全全一樣啊 就不知道字元串數組是怎麼賦值了所以第一個問題就是想了解 字元串數組是怎麼賦值的然後我還有疑問既然*p不能改是因為指向了常量區那我為什麼給他分配了地址還是不行? (按我字元串數組的看法 在heap中分配地址就可以改值了結果還是不行)
且更詭異的是我delete只後居然還是可以 我自己的理解是p跳過了給他分配的地址還是直接指向了」abcd「的常量區
那這樣這個new的意義在哪裡?不好意思哈 問題描述的不好 和朋友試了很多東西 但描述不出來。程序猿的痛。。-------------------------------------------------------------------------------------------------------------------------------------------- 573xmcgcg 答主說 不能改是因為
p[2] = "h"; //錯誤,p不是數組名字
但是上面錯誤是說不能修改const char的值 所以說p[2] = "h";不能修改會不會和p指向const char的值也有關?
PS 我如此執著字元串常量並不是因為要用這個,我也知道用string可以很好的解決這方面問題,我只是想更加深刻的理解指針和數組。。。。
首先來看這段代碼:
char *p = "abcd";
char a[] = "abcd";
p[2] = "h"; // 1
a[2] = "h"; // 2
我們來還原一下案發現場,當時的情況是這樣的:
| |
+-----+
| "a" | 0xA0 &<-+
+-----+ |
| "b" | 0xA1 |
+-----+ |
| "c" | 0xA2 |
+-----+ |
| "d" | 0xA3 |
+-----+ |
| 0 | 0xA4 |
+-----+ |
| | |
. . |
. . |
. . |
| | |
+-----+ |
| "a" | 0xC0 | `
+-----+ | |
| "b" | 0xC1 | |
+-----+ | |
| "c" | 0xC2 | | 變數 a
+-----+ | |
| "d" | 0xC3 | |
+-----+ | |
| 0 | 0xC4 | /
+-----+ |
|0xA0 | 0xC5 --+ | 變數 p
+-----+
| |
圖中 0xA0 - 0xA4 為「常量區」,0xC0 - 0xC5 是當前棧上的變數。
- p[2] 即 0xA0 + 2 = 0xA2,即試圖去修改所謂「常量區」的東西。
- a[2] 即 0xC0 + 2 = 0xC2,即試圖區修改棧上的內容。
本案的關鍵在於,這兩種初始化的方式是不同的。另外,養成用 char const * 去指向「常量區」字元串的習慣,編譯期就可以提醒你這種奇怪的錯誤。
而後的代碼:char *p = new char(7);
p = "abcdef";
p[2] = "h";
delete[] p;
翻譯一下:
- 申請一個位元組的內存,將內容賦值為 0x07。將 p 的內容置為申請到的內存地址。
- 將 p 的內容置為「常量區」字元串常量 "abcdef" 的地址。
- 把地址為 p 的內容 + 2 處的 char 賦值為 "h"。
- 釋放地址為 p 的內容的內存塊。
「詭異」的其實是你的代碼。看出來了嗎?
- 你只申請了 1 個位元組的內存,而不是 7 個
- 得到申請到的內存後,沒有使用就直接丟掉了(把 p 賦值成了「常量區」的地址)
- 核心錯誤依舊在於修改「常量區」的內容
- 試圖釋放常量區的內存
- 沒有釋放申請到的內存
p = "abcdef";
這行只是讓p指向該常量,不會寫入前一行分配的內存。所以後面一行就segfault了。而且這個寫法在C++11里已經是deprecated的了。
把這行換成memcpy(p, "abcdef", 7);
就可以了。
char* p = "abcd";// line 1
這line 1,確實將p指向了常量區。char p [] = "abcd";//line 2這line 2,才是你所說的,在棧里開闢了數組空間。因此,題主的第一個問題,「既然*p不能改是因為指向了常量區」,這句話是沒錯的。而題主的第二個問題,先不說new 的時候char(7)和char[7]的問題,
char * p = new char[7];// line 3line 3,開闢了一個長度為7的字元數組,然後p作為一個指針,指向了這個數組的第0位。目前為止是沒有錯誤的。然後p = "abcd";//line 4line 4的效果等同於line 1,執行之後p指向了常量區。而你剛才new出來的數組呢?呵呵,其實已經永遠找不回來了!!!也就是說,內存泄露了。此刻,你再
delete []p;//line 5至於line 5,很遺憾,此刻你要析構常量區?妥妥的報錯!我不明白題主的程序為什麼沒有崩潰,至少我這裡是崩潰了的。環境VS,WIN7
----------------------------------------------我是分割線你看不粗來么---------------------------------
好,現在我們跑到Ubuntu上去試一試gcc怎麼說針對 char * p = "abcdabcdabcd";//line 6char p[] = "abcdabcdabcd";//line 7上述line6和line7這兩種情況,我們分別gcc -m32 -O0 main.c
之後,來看一看它們彙編的區別:80483c1: c7 45 f8 b0 84 04 08 movl $0x80484b0,-0x8(%ebp)這是第一種情況,很顯然,把某個地址賦給了程序棧,好,那我們具體來看一看這個$0x80484b0是什麼鬼。80484b0: 61 80484b1: 62 63 64 80484b4: 61 80484b5: 62 63 64 80484b8: 61 80484b9: 62 63 64很明顯了,這不就是abcd這一串東西的ASCII么(當然,是16進位,0x61=97="a")
於是得到結論,line6這種情況,是棧里有個指針,然後指針指向了常量區。好了,回過頭來看line7這種情況:8048421: c7 44 24 0e 61 62 63 movl $0x64636261,0xe(%esp) 8048428: 64 8048429: c7 44 24 12 61 62 63 movl $0x64636261,0x12(%esp) 8048430: 64 8048431: c7 44 24 16 61 62 63 movl $0x64636261,0x16(%esp) 8048438: 64真的在棧里把abcd這群東西寫進去了好么!蔡傑答得很好了,其實就是淺拷貝和深拷貝的區別,char* a=b是淺拷貝,只有一份副本,在你的問題中,這個副本在常亮區,char a[]=b是深拷貝,有兩個副本,一個在常亮區,一個在棧上。
思而不學則殆
推薦閱讀:
※比較有效的內存清理軟體?高手們都是怎麼手動優化系統和內存的?
※Windows10 TH2中引入的內存壓縮技術是什麼原理?
※Unable to create new native thread 的本質是什麼?