建電大社 [8]
by 鹽亞倫
AKA AaW、忘記要備課的那個講師
先從一個故事開始說起......
很久很久以前,在一個遙遠的森林裡,住著 AaW 和 Brine 兩個人。
有一天,Brine用抓到了一隻雞,他突然突發奇想
如果他拿著一隻雞腿去考數資班,是不是就可以用雞腿換到數資資格,然後他就可以
於是,他要AaW幫他寫一個交換的函數
swap(a, b)
可以讓他拿雞腿換東西交換兩個變數的內容物
地奧選訓前半 & 雙校隊 & 電爆AaW & 在兩個社團瘋狂炸魚 & 地奧金牌 & 資奧金牌 & 保送台大資工系
所以,要怎麼做?
直接來嗎?
#include <iostream>
using namespace std;
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
return;
}
int main() {
int a = 10;
int b = 20;
cout << "before swap:";
cout << "a = " << a << ", b = " << b << endl;
swap(a, b);
cout << "after swap:";
cout << "a = " << a << ", b = " << b << endl;
// output result???
}
為什麼沒有換成功?
變數的作用域!!!
變數的作用域
簡單講,一般寫程式分成全域變數和區域變數
全域變數在整個程式中都可以被使用
區域變數只能在同一個變數中使用
例如:在main函數中宣告的韓數只能在main裡面使用
換句話說:
剛剛swap函數裡面的a和b
和主程式裡面的a和b
其實是兩個不同的變數,各自有各自的作用範圍
因此,swap裏面a, b互換並不會換到main裡面的a和b!
也就是說,swap裡面的a,實質上是把main的a存的值複製並且存入一個新的記憶體空間的新變數!
這種函數呼叫方式稱為 call by value
所以說,到底變數存在電腦裡是長怎麼樣?
這是一個記憶體的示意圖
其中可以看到,記憶體每個變數區塊都有一個編號,稱為記憶體位置
而值則代表變數實際存的東西
如何知道一個變數的記憶體位置呢?
可以用 取址符號 (&)
例如:要看 x 的記憶體位置,你可以
cout << &x << endl;
這是一個剛剛那份code 記憶體的示意圖
可以看到他其實有兩個區塊
main 和 swap
兩個區塊各自有兩個區域變數 a 和 b
#include <iostream>
using namespace std;
void swap(int a, int b) {
cout << "in swap, &a = " << &a << " &b = " << &b << endl;
int temp = a;
a = b;
b = temp;
return;
}
int main() {
int a = 10;
int b = 20;
// print the address of a and b
cout << "in main, &a = " << &a << " &b = " << &b << endl;
swap(a, b);
}
執行這段code來觀察剛剛講的東西
可以看到一樣是 cout << &a;
在swap裡面和main的結果不一樣
也就證明了它並不是同一個變數
pointer
回想一下,如果我們今天在swap函數裡面,
如果我希望直接動到main裡面的變數怎麼辦?
如果我知道main函數的a和b存在記憶體的哪裡,我是不是就可以直接動它!?
把記憶體位置直接傳入函式當中!!!
But How?
指標變數
用來處理剛剛的問題的啦~
在指標變數裡面,存入另一個變數的記憶體位置!
假設我讓一個變數p記錄著x的位置:
所以這個神奇指標型態的p要怎麼用勒?
int *p = &x;
所以 p 的變數型態是
int*
代表 p 是一個整數指標變數
存入 x 的記憶體位置
換句話說,任何型態為 T 的變數 x
我都可以讓 p 為指向 x 的指標
by this :
T* p = &x;
就算 T 本身就是 int* 也可以!
===> 指向指標的指標!
int a = 10;
int *b = &a;
int **c = &b;
好,所以我知道 p 指向 x了
然後勒
要怎麼用 p 去改 x 存的東西?
用取值符號 (*)
例如,將 p 指向的東西 +=10
並且輸出
*p += 10;
cout << *p << endl;
所以知道一開始swap要怎麼改了嗎?
傳入記憶體位置,用指標變數去接
因此宣告函數時:
void swap(int *a, int *b);
使用時:
swap(&a, &b);
這種傳入方式我們稱之為 call by address
完整code
#include <iostream>
using namespace std;
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
return;
}
int main() {
int a = 10;
int b = 20;
cout << "before swap:";
cout << "a = " << a << ", b = " << b << endl;
swap(&a, &b);
cout << "after swap:";
cout << "a = " << a << ", b = " << b << endl;
}
此時的記憶體長相:
reference
也有人叫他參照
不覺得剛剛用指標很煩人很累嗎
我如果只是希望,兩個變數變成完全變成同一個,連用法都一模一樣,有辦法做到嗎?
用參考!!!
宣告:型態& 名稱 = 被參考的變數;
ex.
int& refa = a;
double &rb = b; // &放靠近變數名也可以
要注意的是,這裡的&和剛剛取址符號無關
僅代表我宣告的變數是一個 參考變數!
換句話說, refa、refb 的型態不是int,而是 int&
阿宣告完之後要怎麼使用?
直接用!!!
而且對refa做的變動會同步到a!
int a = 10;
int& refa = a;
cout << refa << endl; // 10
refa = 15;
cout << refa << endl; // 15
cout << a << endl; // 15
a = 25;
cout << refa << endl; // 25
cout << a << endl; // 25
為什麼會這樣?
熟悉的圖
假如我說 int& c = a;
c應該放哪裡?
c
(int &)
電腦會直接將c和a放在同一個記憶體空間!!!
也就是說以下兩句會有一模一樣的輸出!
cout << &a; //
輸出a的記憶體位置
cout << &c; // 輸出c
的記憶體位置
回到最初的swap函數
所以要怎麼寫勒?
#include <iostream>
using namespace std;
void swap(int &a, int &b) { // 改為參考
int temp = a;
a = b;
b = temp;
return;
}
int main() {
int a = 10;
int b = 20;
cout << "before swap:";
cout << "a = " << a << ", b = " << b << endl;
swap(a, b);
cout << "after swap:";
cout << "a = " << a << ", b = " << b << endl;
// output result???
}
成功了!
void swap(int &a, int &b);
之後會常常遇到將物件以參考傳入函數當中
你開心甚至可以把vector用參考傳入函數
這種方式稱為 call by reference
由於使用參考並不需要將記憶體內東西複製,也不用增加新的空間,因此空間和時間用量都很好!
所以這個東東一定要會ㄛ~
學習檢測時間!
& 和 *
這兩個符號知多少?
* | & | |
---|---|---|
宣告時 | 代表我要宣告一個 指標變數 ex. int *a = &b; |
代表我要宣告一個 參考變數 ex. int &a = b; |
非宣告時 |
取值符號 代表我要取得指標指向的變數存的值 ex. cout << *a; |
取址符號 代表我要一個變數的記憶體位置! ex. int *a = &b; ex. cout << &b; |
測驗
cout << a;
cout << &a;
cout << b;
cout << *b;
cout << &b;
cout << c;
cout << &c;
cout << *c;
cout << d;
cout << *d;
cout << **d;
cout << &d
cout << &*d
cout << &**d
int a = 10;
int *b = &a;
int &c = a;
int **d = &b;
今天東西很硬QAQ
回去好好複習喔~
週日秋遊記得來不要忘了ㄚ~