LV. 42
GP 2k

RE:【問題】碩2資訊工程研究所 問 都問

樓主 丁丁跑卡車 qwertyasdf
27 -
終於把神經網路架好惹,趁在訓練時來消業障。
今天來寫下窩對指標(Pointer)ㄉ理解。
度ㄉ度度,指標(Pointer)是資訊學生碰到的第一ㄍ難關,窩接觸過ㄉ資訊人,包含窩自己第一次碰到指標ㄉ想法有三種:

1.這三小
2.這是三小
3.這是三小啦幹

那我們就來看看指標大大泥究竟4三小ㄋ?

首先第一個觀念:
每一個變數需要被記錄在記憶體,泥可以想像有一排櫃子,每ㄍ櫃子裡有放變數。
像這樣子:



櫃子上面有一個編號,這樣才能找到東西。像是上圖就是2228號櫃子裡面有個東西叫做87。

我們來和C程式碼對照:
int mumi = 87 ;
在櫃子中的樣子是這樣:


mumi的數值是87,放mumiㄉ櫃子編號是8000這樣。

上面ㄉ兩條虛線就是我們可以在寫程式時透過mumi這個變數名稱存取mumi的值87,以及mumi的櫃子編號8000。

printf("mumi:%d\n", mumi) ;
如果我們取的是mumi,就會拿到mumi的值87:



printf("&mumi:%p\n", &mumi) ;
如果我們取的是&mumi,那麼就會取到mumi的櫃子編號8000:


豪,那什麼是指標ㄋ?
指標一樣是個放在櫃子裡的東西,放指標的櫃子一樣會有編號。他特殊的地方在於:
1.櫃子裡放ㄉ東西是:「另一個櫃子的編號」
2.可以透過這個編號去開另一個櫃子拿東西

豪知道大家ㄅ喜歡看字,直接上圖:

int* mumiptr  ;



我們宣告ㄌ一個mumiptr,這個mumiptr也是個變數,所以也會被放在櫃子裡,這個櫃子編號是5566。
同樣的,你也可以用mumiptr抓出值,以及用&mumiptr抓出櫃子編號。除此之外,指標變數有一個功能,就是透過他記錄的櫃子編號找到這個櫃子,然後把裡面東西拿出來。

上面這個mumiptr還沒有設定值,我們來給他設定下:
mumiptr = &mumi ;


如同我們之前說ㄉ,&mumi可以取出mumi的櫃子編號8000,我們把mumi的櫃子編號8000指定成mumiptr的值。

printf("mumiptr:%p\n", mumiptr) ;
我們可以取用mumiptr的值,而這個值同時也是mumi的櫃子編號&mumi,也就是8000:


printf("&mumiptr:%p\n", &mumiptr) ;
我們當然也可以取拿來放mumiptr的櫃子編號,也就是&mumiptr,它是5566。


接下來,就是指標最主要的功能了:*mumiptr。這邊要注意不要跟宣告時的int* mumiptr混淆,int* mumiptr是宣告一個int*指標,這個指標叫做mumiptr。
而*mumiptr是:順著mumiptr這個值紀錄的櫃子編號,去把這個櫃子打開,拿出裡面的值。
像這樣子。

printf("*mumiptr:%d\n", *mumiptr) ;

首先,姆咪找到了mumiptr,打開了這個櫃子,裡面寫著8000。
姆咪蹦蹦跳,跳到了編號8000的櫃子,把櫃子打開,拿出了裡面的值:87。
所以,*mumiptr可以取出87這個值。

這邊你可能會注意到,姆咪在打開了mumiptr的櫃子之後,就心動動的去找櫃子,根本不管這櫃子是ㄅ是自己的,這樣4很危險ㄉ。
這樣有什麼危險性呢?我們用個常見的錯誤例子。第一行是我們剛剛的正確寫法,第二行忘了加上&:

int* mumiptr = &mumi ;
int* mumiptr2 = mumi ;
這邊mumi是一個非指標的值,正確的寫法應該是第一行int* mumiptr = &mumi ;
這樣會發生什麼事呢?



printf("mumiptr2:%p\n", mumiptr2) ;
printf("&mumiptr2:%p\n", &mumiptr2) ;
如圖,mumiptr把mumi的值,而不是櫃子編號,當作了櫃子編號存進去了。
mumi的櫃子編號應該是8000,87是值,而mumiptr把87當成櫃子編號,而87櫃子不是姆咪的。
這邊我們如果存取mumiptr,會得到錯誤的櫃子編號87,但是姆咪只是看了櫃子編號,沒有去開櫃子,所以沒關係。

然後姆咪看了看&mumiptr,就是mumiptr的櫃子編號,這也沒事,得到櫃子編號5566。


如果我們用了*mumiptr,那麼會發生什麼事呢?

回顧一下姆咪會做什麼:

首先,姆咪找到了mumiptr,打開了這個櫃子,裡面寫著8000。
姆咪蹦蹦跳,跳到了編號8000的櫃子,把櫃子打開,拿出了裡面的值:87。
所以,*mumiptr可以取出87這個值。
這是剛剛mumiptr被設定成&mumi,也就是8000時姆咪做的事。

現在mumiptr是87。

printf("*mumiptr2:%d\n", *mumiptr2) ;
首先,姆咪找到了mumiptr,打開了這個櫃子,裡面寫著87。
姆咪蹦蹦跳,跳到了編號87的櫃子,把櫃子打……



……


……


然後姆咪就掛惹QQ,留下了最後的遺言:Segmentation fault: 11

所以我們可以看出指標的危險性在於:因為它賦予你根據櫃子編號隨便開櫃子的能力,所以開到錯誤的櫃子時,就會出事。
作業系統不會允許你隨便存取記憶體,就會把你殺掉惹。

最後回顧,順便給大家一個簡單可以編譯、跑的版本:

include<stdio.h>

int main(){
int mumi = 87 ;
int* mumiptr = &mumi ;
int* mumiptr2 = mumi ;
printf("mumi:%d\n", mumi) ;
printf("&mumi:%p\n", &mumi) ;
printf("mumiptr:%p\n", mumiptr) ;
printf("*mumiptr:%d\n", *mumiptr) ;
printf("&mumiptr:%p\n", &mumiptr) ;
printf("mumiptr2:%p\n", mumiptr2) ;
printf("&mumiptr2:%p\n", &mumiptr2) ;
printf("*mumiptr2:%d\n", *mumiptr2) ;
return 0 ;
}

int mumi是宣告整數mumi,int* mumiptr是宣告整數指標mumiptr
mumi是mumi的,&mumi是mumi的位址,也就是放mumi的櫃子編號。

mumiptr是mumiptr的,因為他是指標,所以他的值也是個位址。
&mumiptr是mumiptr的位址
*mumiptr是根據mumiptr的值,找到這個位址,取出裡面的值

以上這個範例程式應該會在執行到printf("*mumiptr2:%d\n", *mumiptr2) ;時,因為試圖存取不是自己的位址而錯誤中止。

我的執行結果:
O-odeMacBook-Pro:algo_test o_o$ ./a.out
mumi:87
&mumi:0x7fff567e9638
mumiptr:0x7fff567e9638
*mumiptr:87
&mumiptr:0x7fff567e9630
mumiptr2:0x57
&mumiptr2:0x7fff567e9628
Segmentation fault: 11

記憶體位址會隨著每次執行改變,所以你拿去跑,0x那串應該會是不一樣的。

圖片來源:
https://static.ettoday.net/images/2312/d2312999.jpg
https://i.ytimg.com/vi/JQlRVZejDlo/hqdefault.jpg
https://www.daiso.com.tw/upload/product/64221/498435507713801.JPG
27
-
未登入的勇者,要加入討論嗎?
板務人員:

7582 筆精華,11/16 更新
一個月內新增 13
歡迎加入共同維護。


face基於日前微軟官方表示 Internet Explorer 不再支援新的網路標準,可能無法使用新的應用程式來呈現網站內容,在瀏覽器支援度及網站安全性的雙重考量下,為了讓巴友們有更好的使用體驗,巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現和功能。
屆時建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業系統版本才可使用)

face我們了解您不想看到廣告的心情⋯ 若您願意支持巴哈姆特永續經營,請將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學】