指標本質論

2021-08-27 06:22:08 字數 4330 閱讀 3171

指標本質論 

1.指標是什麼? 和一般變數有什麼區別?

指標就是位址,和一般變數沒有本質區別,僅僅是它有自己的規則。

int a=100;

int *p=&a;

printf("%d\n",a); // 100

printf("%p\n",p); //0xbfa47858

a是乙個變數名,型別是int,值是100,a有自己的位址&a

p是乙個變數名,型別是int*,值是0xbfa47858,p有自己的位址&p

我們發現指標和普通變數沒什麼區別,那到底為什麼我們覺得它難呢?

主要是指標是一種新的變數型別,它有自己的一套運算規則。

2.指標的運算規則

#1.指標的型別

定義不同的指標,它有自己的型別. 如,

int *p -> int 型指標

char *p -> char 型指標

int (*p)[2] -> int型的陣列指標

void (*p)() -> 函式指標

...果然很多,怪不得那麼難.

#2.指標的強制型別轉換

int *p;

char *q=(char*)p;

這是把int型指標轉換成char型指標,和變數的強制型別轉換一樣,so easy...

#3.指標的加減規則

指標有自己一套加減規則。

int a=100;

int *p=&a;

printf("%d\n",a); // 100

printf("%p\n",p); //0xbfa47858

p=p+1; p是int型指標,sizeof(int)=4,所以p+1其實加的是乙個int型變數的大小,所以p=0xbfa4785c

char *p;

p=0x77777777; //p是乙個變數,可以給p直接賦值

p=p+1; p是char型指標,sizeof(char)=1,所以給p+1其實加的是乙個char型變數的大小,所以p=0x77777778

....

我們發現,還是so easy...

#4. 指標的解引用,也就是*p

解引用是有步驟的

如 type *p; 求*p //type是型別

1.找到指標所存放的記憶體單元

2.從這個位址開始讀取sizeof(type)個位元組大小

3.把讀取的內容按type型別解釋,解發布來的就是*p

eg.short a=256;

short *p=&a;

printf("%d\n",*p); //256,so easy...

1.p所對應的就是a的位址

2.a->00000001 00000000,讀取2個位元組,所以讀取到的就是00000001 00000000

3.把00000001 00000000解釋成為short型別,明顯是256

char *q=(char*)p;

printf("%d\n",*q); //0, maybe not so easy....

1.p所對應的就是a的位址

2.a->00000001 00000000,讀取1個位元組(sizeof(char)),所以讀取到的就是00000000(小段位元組序)

3.把00000000解釋成為char型別,明顯是0

如果上面的都懂了,那麼下面的就不是難事

我要讀取a的高位址,怎麼寫?

char *q=(char*)p+1; //(char*)p指向a的地位址,+1,因為是char型指標,所以加乙個位元組,指向a的高位址

printf("%d\n",*q); // 讀取sizeof(char)個位元組,即00000001,所以是輸出1

short a=256;

char *p=&a;

printf("%d\n",*(short*)p);

p指向a的低位址,將p強制轉換為short型製作,所以讀取sizeof(short)個位元組,所以輸出256

short a=256;

char *p=&a;

printf("%d\n",*((char*)(short*)p+1)); //輸出結果為1,so easy...

再來點有挑戰的吧!

short a=256;

char *p=&a;

printf("%d\n",*(short*)(*(int*)&p)); //256

如果這道題目做對了,那麼指標也就被你俘虜了。

關鍵就是這是為什麼是int*,主要是指標所佔空間是4個位元組(32位主機)

#5.那麼陣列指標和指標陣列到底怎麼回事?

陣列指標是指標,指標陣列是陣列,就這區別

陣列指標只存在與多維陣列嗎?,下面將帶給你不一樣的認識

我們知道陣列名所代表的就是陣列首位址,那麼對陣列名取位址得到的是什麼?

一維陣列:

int a[2]=;

(gdb) p a

$5 =

(gdb) p &a[0]

$6 = (int *) 0xbffff0c0 //&a[0]是普通int型指標

(gdb) p &a[0]+1

$7 = (int *) 0xbffff0c4 //所以+1加的是sizeof(int)

(gdb) p &a

$8 = (int (*)[2]) 0xbffff0c0 //一維陣列名的位址其實是個陣列指標,這個就有點像sizeof(a)返回的是整個陣列大小

(gdb) p &a+1

$9 = (int (*)[2]) 0xbffff0c8 //所以+1加的是2*sizeof(int)

(gdb) p a+1

$10 = (int *) 0xbffff0c4 //我們發現當陣列名用於表示式時自動轉換為陣列首位址

二維陣列:

int a[2][2]=;

(gdb) p a

$1 = , }

(gdb) p &a[0]

$2 = (int (*)[2]) 0xbffff0b8 //因為是二維陣列,所以&a[0]是陣列指標

(gdb) p &a[0]+1

$3 = (int (*)[2]) 0xbffff0c0 //所以+1,加的是2*sizeof(int)

(gdb) p &a

$4 = (int (*)[2][2]) 0xbffff0b8 //對陣列名取位址代表的是整個陣列的首位址,有點整體的意思

(gdb) p &a+1

$5 = (int (*)[2][2]) 0xbffff0c8 //所以+1加的是sizeof(a)

(gdb) p a==&a

$7 = 1

(gdb) p a==&a[0]

$8 = 1

(gdb) p a==&a[0][0]

$9 = 1

其實從值的角度來說,a,&a,&a[0],&a[0][0]都是陣列首位址的值

int a[2][2]=;

int (*p)[2]=a;

如果我要輸出3:

printf("%d\n",**(p+1)); //3

這個p已經是指標了,為啥要**呢,難道這是指標的指標?

在一維陣列中我們知道: *(a+i)=a[i],在二維陣列中同樣成立.

*(p+1)=a[1],只不過此時的a[1]代表的是第二行的首位址,所以會出現**的問題.

printf("%d\n",*(int*)(p+1)); // 3,這樣寫我想更清楚,易懂

我們來點難點的吧?

short a[2][2]=;

short *q=a[1];

printf("%d\n",**(char(*)[2])q); // 0,你對了嗎?

q指向的是a[1]首位址,即256,然後將256當作2列char型陣列,00000001 00000000,

所以最後輸出來的是低位址的值->0

再來乙個更**的吧!!

char a[2][4]=;

short *q=a;

printf("%d\n",*(*(((short(*)[2])q)+1)+1)); //小段位元組序算

答案是258,你做對了嗎?

解釋:(short(*)[2])q -> 將q轉換為short型的陣列指標

*(((short(*)[2])q)+1) -> 此時指向的是4

*(*(((short(*)[2])q)+1)+1) -> 此時指向的是2,00000010 00000001,而00000010是低位址,

所以最後為00000001 00000010 -> 258

繼承本質論

1.引言 關於繼承,你是否駕熟就輕,關於繼承,你是否瞭如指掌。本文不討論繼承的基本概念,我們回歸本質,從編譯器執行的角度來揭示.net繼承中的執行本源,來發現子類物件是如何實現了對父類成員與方法的繼承,以最為簡陋的示例來揭示繼承的實質,闡述繼承機制是如何被執行的,這對於更好的理解繼承,是必要且必然的...

繼承本質論

原創作品,轉貼請註明作者和出處。關於繼承,你是否駕熟就輕,關於繼承,你是否瞭如指掌。本文不討論繼承的基本概念,我們回歸本質,從編譯器執行的角度來揭示.net繼承中的執行本源,來發現子類物件是如何實現了對父類成員與方法的繼承,以最為簡陋的示例來揭示繼承的實質,闡述繼承機制是如何被執行的,這對於更好的理...

type traits 之 本質論

侯捷老師在 stl 原始碼剖析 說 traits程式設計方法是一把開啟stl源 大門的鑰匙,其重要性也就不必再說了。既然traits程式設計方法如此重要,那麼掌握並領悟其精髓是相當必要了。trait的意思是什麼?英文意思是attribute,feature等等,中文意思可以解釋為特點,特性。那麼ty...