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

  1. "&":取址符號,接在變數前面可以知道一個變數存的位置。
  2. "*":指標變數型態,宣告變數時放在變數前面即可。
  3. "*":取值符號,接在變數前面可以存取一個位置存的值,包括對值做操作。
  4. 陣列也是指標的一種

Conclusion

大家可能還沒感覺到指標的用處。

但他其實是很多東西重要的基礎,是很多進階的東西(像是下禮拜教到的一些簡單資料結構)都會用到,一開始可能會有點抽象有點難沒關係,有問題問就對了!

Pointer

By Wu-Jun Pei

Pointer

  • 314