Pointer
Wu-Jun Pei@Sprout2018
變數是怎麼儲存的?
變數是怎麼儲存的?
相信大家都知道電腦除了長在你面前的螢幕、滑鼠之外,主機裡面的 CPU、記憶體才是真正幫助運算的角色。
當中記憶體的功能就是紀錄我們所使用到的各種變數。
變數是怎麼儲存的?
儲存的方式大概會是每個變數開一個他需要用的大小(像是 int 有 4 byte,可以用 sizeof 查看),然後電腦就會記住這個變數的記憶體位置,以利之後的使用。
剩下的你就不用知道了
生活化的例子
今天我掌管了德田館(各位現在在的這棟,想像成是一大塊記憶體),想像每間房間都是存放變數的空間。
今天程式宣告一個整數 (int) 變數 abc = 123,存在204。
如果使用者要更改 abc 成 456,就是更改 204 這塊記憶體,把這塊記憶體的資料改成 456(想像成我可以在飛快的時間衝到教室把裡面存的 123 改成 456)
生活化的例子
如果今天又宣告一個 int 變數叫做 haha = 87,存在 304,那就可以想成是我飛快的衝到 304 並把 87 存到裡面。
生活化的例子
如果你今天想問 204 這個位置存什麼的話,我就會飛快的衝到 204,知道 204 存著 456,然後告訴你。
生活化的例子
如果今天你問我 404 裡面是多少?
一樣的,我會飛快的衝到 404 看裡面存什麼,但裡面存什麼就不保證了(可能是某次大戰留下的遺跡)。
註:在多數的情況下,亂戳記憶體不會是好的事情,很長會發生「應用程式已停止作業」
生活化的例子
註:
實際上,宣告變數時電腦會幫你找好一塊記憶體使用,不用指定。
飛快的:代表著忽略距離,很快的意思
寫成 code - 取址
寫成 code - 取址
從知道一個位置是什麼開始!
使用方法:
"&"符號(shift + 7)有一些特別的功用,其中一個是「取址」
int a;
std::cout << &a << std::endl;
0x7ffee412b76c 是我得到的結果(做投影片的時候),0x 開頭代表是一個 16 進位的數字,沒什麼特別意義,只是 abc 住的位置而已。
寫成 code - 取址
寫成 code - 取址
int a = 1, b = 2, c = 3;
std::cout << "a stores in " << &a << ", its value is " << a << std::endl;
std::cout << "b stores in " << &b << ", its value is " << b << std::endl;
std::cout << "c stores in " << &c << ", its value is " << c << std::endl;
std::cout << "a uses " << sizeof(a) << " bytes" << std::endl;
執行後比較跟旁邊的人有沒有使用一樣的記憶體位置
執行多次有沒有使用一樣的記憶體區塊
sizeof 練習:在自己寫的 struct 上使用 sizeof 試試看!
練習:
寫成 code - 指標
寫成 code - 指標
機會稍縱即逝,沒有好好看好剛剛那個位置就不見了,好想存起來喔~
指標派上用場了!
寫成 code - 指標
介紹指標:
有別於「取址」,指標是一種資料型態
還記得資料型態像是 int, char 嗎?分別拿來存整數以及字元。
指標是一種專門拿來存位置的資料型態!
寫成 code - 指標
使用方法:
"*"星星符號(shift + 8)除了乘法外也可以作為指標。
int abc = 123;
int *ptr = &abc;
std::cout << "&abc = " << &abc << std::endl;
std::cout << " ptr = " << ptr << std::endl;
std::cout << "ptr uses " << sizeof(ptr) << " bytes" << std::endl;
寫成 code - 指標
注意到:
1. 我把 * 放在變數 ptr 的前面,而不是放在 int 的正後面,兩種方法都是可以接受的,個人習慣為主(見下頁code)
2. 在印出 ptr 的時候,不需要在額外加特殊的符號,因為它就是一個指標變數,本來就會印出一個位置!
3. 指標變數使用 8 byte
寫成 code - 指標
int abc = 123, def = 456;
int* ptr1 = &abc, ptr2 = &def;
我喜歡讓指標 * 跟著變數!
int abc = 123, def = 456;
int *ptr1 = &abc, *ptr2 = &def;
上面個程式碼片段會CE
這是可以被接受的
寫成 code - 指標
如果我今天有一個位置,我想知道裡面存什麼怎麼辦?
Recall: 查詢、甚至修改位置在 204 的值
寫成 code - 指標
"*"星星符號的第二個功能:「取值」
使用方法:
int abc = 123;
int *ptr = &abc;
std::cout << "*ptr = " << *ptr << std::endl;
寫成 code - 指標
練習:
int abc = 123;
int *ptr = &abc;
std::cout << " abc = " << abc << std::endl;
std::cout << "&abc = " << &abc << std::endl;
std::cout << " ptr = " << ptr << std::endl;
std::cout << "*ptr = " << *ptr << std::endl;
std::cout << "-----------------------" << std::endl;
(*ptr) = 456;
std::cout << " abc = " << abc << std::endl;
std::cout << "*ptr = " << *ptr << std::endl;
寫成 code - 指標
注意到:
如果改變到 ptr 那個位置存的值,因為那裡存著 abc,所以也會跟著改變!
註:你也可以對 ptr 取址,存到 ptr2 中......
陣列
陣列
大家還記得陣列嗎?
void PrintArray(int fib[], int N) {
std::cout << "Fibonacci numbers:" << std::endl;
for (int i = 0; i < 10; i++) {
std::cout << fib[i] << " ";
}
std::cout << std::endl;
}
int main() {
int fib[10] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34};
PrintArray(fib, 10);
}
陣列
其實,陣列的那個 fib 也是一個指標
Value | 0 | 1 | 1 | 2 | 3 | 5 | ... |
Addr | fib | fib + 1 | fib + 2 | fib + 3 | fib + 4 | fib + 5 | ... |
其中,fib 存的位置就是這個陣列的開頭(第零項)的位置
陣列
Code Here
Conclusion
Conclusion
- "&":取址符號,接在變數前面可以知道一個變數存的位置。
- "*":指標變數型態,宣告變數時放在變數前面即可。
- "*":取值符號,接在變數前面可以存取一個位置存的值,包括對值做操作。
- 陣列也是指標的一種
Conclusion
大家可能還沒感覺到指標的用處。
但他其實是很多東西重要的基礎,是很多進階的東西(像是下禮拜教到的一些簡單資料結構)都會用到,一開始可能會有點抽象有點難沒關係,有問題問就對了!
Pointer
By Wu-Jun Pei
Pointer
- 314