Arvin Liu @ 2020 資訊之芽 語法班
第0個櫃子
第1個櫃子
第2個櫃子
「放學校的書的櫃子」
「放輕小說的櫃子」
「放『男人都會有一些不可告人的本子』的櫃子」
放了國文課本。
放了「涼宮春日的憂鬱」。
放了
??????。
它在哪裡? | 它是什麼? | 它放了什麼? |
---|---|---|
第0個櫃子 (0x00) | 放學校的書的櫃子 | 國文課本。 |
第1個櫃子 (0x01) | 放輕小說的櫃子 | 涼宮春日的憂鬱。 |
第2個櫃子 (0x02) | 放『男人都會有一些不可告人的本子』的櫃子 | ????? |
地址。我們通常用十六進位表示。
變數名稱。
就....它放/存了什麼。
int a = 5;
int a = 5;
它在哪裡? | 它是什麼? | 它放了什麼? |
---|---|---|
0x00000 |
int x | 7 |
0x00001 | 空的。 | 沒放東西。 |
0x00002 | 空的。 | 沒放東西。 |
int a
5
它在哪裡? | 它是什麼? | 它放了什麼? |
---|---|---|
0x00000 | int x | 7 |
0x00004 | float a | 1.1 |
0x00008 | double b | 1.0 |
那麼 &x 就是 0x00000。
&
*
但這裡有個疑問,&x是什麼型態?
所以 & 可以把int 變成 int* , * 可以把int*變回int
所以 & 可以把float 變成 float* , * 可以把float*變回float
它在哪裡? | 它是什麼? | 它放了什麼? |
---|---|---|
0x00000 | int x | 7 |
0x00004 | float a | 1.1 |
0x00008 | double b | 1.0 |
那麼 &x 就是 0x00000。
*(0x00000) 呢? 會編譯失敗。 因為0x00000是int,不是int*
*(int *)(0x00000) 呢? 7
*(float *)(0x00004) 呢? 1.1
&*(float *)(0x00004) 呢? 0x00004
一個是型態的一部分 (拿來當宣告用的),
一個是取值符號(拿來使用,當運算的)。
#include <iostream>
int main(){
int x=777;
std::cout << &x << std::endl;
//std::cout << *(int *)(0x7fffffffdc54) << std::endl;
std::cout << *&x << std::endl;
std::cout << &*&x << std::endl;
std::cout << *&*&x << std::endl;
//std::cout << *(0x7fffffffdc54) << std::endl; // CE
//std::cout << *x << std::endl; // CE
//std::cout << **&*&*&x << std::endl; // CE
//std::cout << *(int *)x << std::endl; // RE
}
/* Output -- Answers are here.
0x7fffffffdc54
777
777
0x7fffffffdc54
777
*/
除了練習上面的東西,觀察你變數的地址以外,
試試看 *(int *) 一個隨便給的數字會怎麼樣?
所以你們直接註解那行掉就可以囉!
* 小知識:現今電腦都有ASLR(位址空間組態隨機載入)保護機制,所以每一次執行的&x都不一樣。除非你把它關掉,你才可以預先知道&x是什麼。這邊看不懂沒關係,想了解可以自行google看看。
也就是 - 指標宣告
如果你要存int的地址,
那麼它型態就是 int * 。
它在哪裡? | 它是什麼? | 它放了什麼? |
---|---|---|
0x00000 | int x | 7 |
0x00008 | (int *) a | 0x00000 |
0x00010 | (int **) b | 0x00008 |
int x = 7;
int *a = &x;
int **b = &a;
std::cout << *a << std::endl;
*a = 5;
std::cout << **b << std::endl;
5
**b = x , *b =a , *a = x
int* a, b;
// 上下兩個等價
int *a;
int b;
// 所以宣告兩個指標要
int *a, *b;
所以一個byte 就是 00000000 ~ 11111111
通常bit都會八個一組,我們稱之為byte。
#include <iostream>
int is_admin = 0;
int ary[160];
int main(){
std::cout << sizeof(char) << std::endl;
std::cout << sizeof(int) << std::endl;
std::cout << sizeof(long long) << std::endl;
std::cout << sizeof(float) << std::endl;
std::cout << sizeof(int *) << std::endl;
std::cout << sizeof(double *) << std::endl;
}
// 1 4 8 4 8 8
#include <iostream>
int a;
int main(){
char c;
// 想辦法算出 &a - &c
}
hint : 我們知道指標的大小是 8 bytes,和longlong一樣。
answer : (long long) &a - (long long) &c
Segmentation Fault (會吃RE)
它在哪裡? | 它是什麼? | 它放了什麼? |
---|---|---|
0x00000 | int x | 7 |
0x00004 | 系統的祕密。 | 不給看&寫>< |
0x00008 | 還沒規劃的地方。 | ??????? |
*(int *)0x00004 或 *(int *)0x00008 因為這兩塊還沒規劃或者電腦不給你看,所以當你在*的時候就會出現記憶體區段錯誤。
(例如程式已停止回應。)
所以只要你 * 的位置是系統開給你的,例如宣告變數/陣列,那麼通常就不會出現RE。
int a = 5;
它在哪裡? | 它是什麼? | 它放了什麼? |
---|---|---|
0x00000 | 空的。 | 沒放東西。 |
0x00004 | 空的。 | 沒放東西。 |
0x00008 | int a | 5 |
之後程式自己的記憶體會長這樣子:
int a[4] = {0};
那這樣子呢?
它在哪裡? | 它是什麼? | 它放了什麼? |
---|---|---|
0xFFF00 | a[0] | 0 |
0xFFF04 | a[1] | 0 |
0xFFF08 | a[2] | 0 |
0xFFF0A | a[3] | 0 |
... |
大概會像這樣子:
int a[4] = {0};
編譯器會讓 a 和 &a 都會等於 0xFFF00, 但 *a 是 a[0]。
int a[4] = {0};
a[1] = 1;
追根究底,a[1]是什麼意思?
其實就是 *(a + 1) 的意思。(a 是類似int *,詳情請見上一頁)
那,a + 1 是什麼意思呢?
就是a這個指標的地址 + 1個int的大小。
根據上面的原則。寫a[1]或寫1[a] 都沒有關係喔!
因為 1 + a = a + 1。
一個型態的大小可以用sizeof(type)來看。例如sizeof(int)就會是4,表示4Bytes。
#include <iostream>
int main(){
int ary[10];
std::cout << ary << std::endl;
// 0x61fee8
std::cout << ary + 1 << std::endl;
// 0x61feec
std::cout << (long long)ary + 1 << std::endl;
// 6422249
std::cout << (int *)((long long)ary + 1) << std::endl;
// 0x61fee9
}
例如我們宣告 int ary[160];
但是題目的N可能會到10000,
因此你就會存取到ary[1600]。
它在哪裡? | 它是什麼? | 它放了什麼? |
---|---|---|
0x00000 | int x | 7 |
0x00004 | 系統的祕密。 | 不給看&寫>< |
0x00008 | 還沒規劃的地方。 | ??????? |
也就是你戳到0x00004/0x00008的時候,電腦就會因為安全因素讓你RE。
它在哪裡? | 它是什麼? | 它放了什麼? |
---|---|---|
0xF0000 | ary[0]在這裡。 | 0 |
0xF0004 | ary[1]在這裡。 | 0 |
.... | ... | ... |
0xF003C | ary[159]在這裡。 | 0 |
0xF0040 | 還沒用到 | ??? |
... | ... | ... |
0xF0400 | 系統的祕密>///< | 不給尼看>///< |
因為程式沒幫你開到0xF0400,
只要那邊有系統的祕密/還沒規劃,看了就會吃RE!
#include <iostream>
int ary[160];
int is_admin = 0;
int main(){
int i,x;
std::cin >> i >> x;
ary[i] = x;
if(is_admin){
std::cout << "How did you do that?\n";
}
}
你要輸入什麼才可以讓程式輸出
"How did you do that" 呢?
#include <iostream>
int is_admin = 0;
int ary[160];
int main(){
int i,x;
std::cin >> i >> x;
ary[i] = x;
if(is_admin){
std::cout << "How did you do that?\n";
}
}
你要輸入什麼才可以讓程式輸出
"How did you do that" 呢?
#include <iostream>
#include <cstring>
struct person{
char name[16];
int is_admin;
};
int main(){
char tmp[16];
struct person peipei;
peipei.is_admin = 0;
std::cin >> tmp ;
strcpy(peipei.name,tmp);
std::cout << "Hi! " << tmp << std::endl;
if(peipei.is_admin){
std::cout << "Wow! Peipei is so Dian!" << std::endl;
}
}
你要輸入什麼才可以讓程式輸出
"Wow! Peipei is so Dian!" 呢?
#include <iostream>
long long ary[160];
int is_user=0;
int is_admin=0;
int main(){
long long i,x;
std::cin >> i >> x;
ary[i] = x;
if(is_admin == 1){
std::cout << "Why Peipei is so dian?\n";
}
}
你要輸入什麼才可以讓程式輸出
"Why Peipei is so dian?" 呢?
例如 0x00000100000000 =
4294967296 (輸入4594967296即可。)
表示成二進位制後逆著寫。
詳情請查詢 little-endian
(big-endian 就是順著寫)
longlong 存成
????????10000000
is_user 存 ????????
is_admin 存 10000000
-> is admin 是 00000001 = 1 (逆著)
#include <iostream>
void put_something(){
int ary[1]={0}, i, x;
ary[i] = x;
}
bool can_access(){
int is_admin = false;
put_something();
return is_admin;
}
int main(){
if(can_access()){
std::cout << "Why Peipei is so dian?\n";
}
}
你要輸入什麼才可以讓程式輸出
"Why Peipei is so dian?" 呢?