c語言指標內容為什麼無故改變

時間 2021-05-30 21:47:17

1樓:暮無井見鈴

return 語句返回 name 所隱式轉換而得的指向 name 首元素( name[0] )的指標。從 main2 退出的時候,其中 name 物件的儲存期和生存期就結束了。語義上我們可以認為這次呼叫中為 name 分配的儲存在退出函式後就是沒了。

從而這個指標值變成懸垂了,通過該指標值解引用是未定義行為。實現中同樣一塊儲存(棧空間)可以被復用,並且被後續的其他函式呼叫寫入。

另外注意一下指標和陣列是完全不同的東西。 C 中它們的聯絡只有陣列能隱式轉換成指向陣列首元素的指標,而下標運算子是只定義在指標型別上的。所以你這裡用 sizeof(str)/sizeof(*str) 試圖求陣列長度的寫法是錯誤的。

2樓:qin meng

1.main2返回了區域性指標變數。返回變數是在棧上,呼叫main2返回後如果不呼叫其他函式直接輸出,有一定概率可以正常使用,前提是對應返回值的棧位址沒有被其他的過程破壞掉。

呼叫printf後,大概率會被破壞掉。這是你問的問題的根本原因。

2.sizeof(str)/sizeof(str)不是字串長度,str是char 型,sizeof(str)長度是機器字長,32位是4,64位是8,sizeof(*str)是str指向第乙個內容,是個char,所以是1。

3.main2返回型別是char*,但是name 是const char *。

3樓:

這種事不一定發生。編譯器可能會將main2函式內聯展開優化,變為直接在main函式載入"abc.h"字串,這樣一來字串的空間在main函式上被預留出來,內容就不會被printf函式的呼叫覆蓋掉。

Visual C++ 2019的/O2優化就是這樣做的。

下面附上Visual C++生成的main函式的反彙編,編譯器版本為19.28.29335,配置為Release|Win32:

00E71040

push

ebp00

E71041

movebp

,esp

; main2函式被優化成載入靜態字串"abc.h"

00E71043

subesp,8

; 給字串預留了8位元組(考慮記憶體對齊),esp = ebp-8

00E71046

moveax

,dword

ptr[

string

"abc.h"

(0E87A8Ch

)]; 把靜態字串的前4位元組複製到eax

00E7104B

movdword

ptr[

ebp-8],

eax; 把靜態字串的前4位元組放到ebp-8,也就是esp所在的位址

00E7104E

movax

,word

ptrds

:[00E87A90h

]; 把靜態字串的後2位元組複製到ax

00E71054

push

esi; esp = ebp-12

00E71055

push

offset

string

"\xb2\xe2\xca\xd4"

(0E87A94h

); 將字串"測試"的位址傳參,esp = ebp-16

00E7105A

movword

ptr[

ebp-4],

ax; 把靜態字串的後2位元組放到ebp-4(esp+12),緊接前4位元組之後

00E7105E

call

printf

(0E71010h

); 呼叫printf,因為載入後的字串在esp之後,所以不會被破壞

00E71063

addesp,4

00E71066

xoresi

,esi

00E71068

nopdword

ptr[

eax+

eax]

00E71070

movsx

eax,

byte

ptr[

ebp+

esi-8]

00E71075

push

eax00

E71076

push

offset

string

"%c"

(0E87A9Ch)00

E7107B

call

printf

(0E71010h)00

E71080

incesi

00E71081

addesp,8

00E71084

cmpesi,4

00E71087

jlmain

+30h

(0E71070h)00

E71089

xoreax

,eax

00E7108B

popesi

00E7108C

movesp

,ebp

00E7108E

popebp

00E7108F

ret這裡就試著按照沒有上述優化的情況來看看題主的環境下可能發生了什麼,從而看出錯誤做法的後果。

假設題主將程式編譯為32位x86可執行檔案,這種情況下棧是從高位址向低位址生長的,且指標的長度為32位(4位元組)。

[返回位址,4位元組][函式呼叫前的位置]

[棧基位址,4位元組][返回位址,4位元組][函式呼叫前的位置]

然後函式為"abc.h"字串預留空間並填充內容:

["abc.h",8位元組(記憶體對齊到4位元組)][棧基位址,4位元組][返回位址,4位元組][函式呼叫前的位置]

然後返回字串的位址。可以看出,函式中的name陣列位於函式呼叫前棧指標所在位置向低位址偏移14個位元組之後的位置。

接下來呼叫printf函式。假設"測試"字串位於靜態資料區。根據C語言實際使用的呼叫約定(__cdecl),先將引數的位址入棧:

["abc.h",8位元組(記憶體對齊到4位元組)][棧基位址,4位元組]["測試"的位址,4位元組][函式呼叫前的位置]

["abc.h",8位元組(記憶體對齊到4位元組)][返回位址,4位元組]["測試"的位址,4位元組][函式呼叫前的位置]

['a',1位元組]['b',1位元組]['c',1位元組]['.',1位元組][棧基位址,4位元組][返回位址,4位元組]["測試"的位址,4位元組][函式呼叫前的位置]

此時字串陣列中的內容已經被破壞了。printf函式內部有可能呼叫了其他子函式,每次呼叫至少要再入棧8位元組(返回位址和棧基位址),那麼整個字串的內容都將不保。

這就是指標指向的內容改變的原因。如果沒有printf("測試");呼叫,之前main2函式中的name字串就不會被覆蓋掉,自然不會輸出錯誤的內容。

順帶一提,sizeof(str)得到的是char *指標型別的大小,而不是陣列的大小。在32位x86平台上結果固定為4,64位x86平台上結果固定為8。對於題主的字串而言,32位平台下無法輸出字串的全部內容,64位平台下會發生陣列越界,無論如何都是不對的。

即使使用其他方法得到了陣列的長度,也需要考慮字串末尾的空字元,將長度減一。正確的做法是使用strlen函式獲得字串的長度。

(C語言)位址為什麼要變成指標才能賦值給指標變數呢?

szouc C語言中所有資料都具有兩個屬性,乙個是值,另外乙個是型別。即使值相同如果型別不一致也是不同的資料。有一道題 小區內有乙隻狗叫Oscar,有乙隻貓也叫Oscar,大晚上有人喊 Oscar 請問他找誰?Oscar 找我幹嘛?位址是乙個整型資料,而指標具有 指向物件型別的指標 的型別。因此即使...

為什麼說指標是 C 語言的精髓?

悽臨雨 指標 型別 虛擬記憶體位址值 整數 指標運算 根據型別確定的寬度對位址值進行修改。其中c語言裡的型別,比c 裡的型別的功能弱的多,所以你覺得c的精髓是指標,在c 裡就是個基礎操作,沒什麼大不了的。因為c語言的語言功能太貧瘠,所以只能靠記憶體位址的修改來做事,沒它就寸步難行。 Howard J...

為什麼指標作為c語言的靈魂,用的地方卻很少甚至幾乎不用?

紫葡萄 很多地方會用的,最簡單的乙個應用,函式多返回值問題 比如乙個函式要返回4個數,像go支援函式多返回值 其實底層也是用指標實現的 在C中只能用C指標做,傳入4個指標,在函式內修改指標的值 小北 然後說用處不大,這是題主自己的理解吧,實際上工程裡根本離不開指標,這個展開講就太豐富了。等題主上過計...