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;

翻譯一下:

  1. 申請一個位元組的內存,將內容賦值為 0x07。將 p 的內容置為申請到的內存地址

  2. 將 p 的內容置為「常量區」字元串常量 "abcdef" 的地址。

  3. 把地址為 p 的內容 + 2 處的 char 賦值為 "h"。

  4. 釋放地址為 p 的內容的內存塊。

「詭異」的其實是你的代碼。看出來了嗎?

  • 你只申請了 1 個位元組的內存,而不是 7 個

  • 得到申請到的內存後,沒有使用就直接丟掉了(把 p 賦值成了「常量區」的地址)

  • 核心錯誤依舊在於修改「常量區」的內容

  • 試圖釋放常量區的內存

  • 沒有釋放申請到的內存

p.s. 這真不是一個 C++ 問題,這是一個 C 語言問題。


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 3

line 3,開闢了一個長度為7的字元數組,然後p作為一個指針,指向了這個數組的第0位。目前為止是沒有錯誤的。然後

p = "abcd";//line 4

line 4的效果等同於line 1,執行之後p指向了常量區。而你剛才new出來的數組呢?呵呵,其實已經永遠找不回來了!!!也就是說,內存泄露了。此刻,你再

delete []p;//line 5

至於line 5,很遺憾,此刻你要析構常量區?妥妥的報錯!我不明白題主的程序為什麼沒有崩潰,至少我這裡是崩潰了的。

環境VS,WIN7

----------------------------------------------我是分割線你看不粗來么---------------------------------

好,現在我們跑到Ubuntu上去試一試gcc怎麼說

針對

char * p = "abcdabcdabcd";//line 6

char 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 的本質是什麼?

TAG:編程語言 | 編程 | 內存管理 | C | C指針 |