指针类型和指针类型转换的理解
前幾天在判斷??“值相同的兩個(gè)指針?biāo)赶虻淖兞康闹悼梢圆煌?strong>?”??這句話時(shí),發(fā)現(xiàn)自己對(duì)指針類型一些概念僅僅是記住了結(jié)論。于是查閱了一些資料,記錄一下一些與指針類型和指針類型轉(zhuǎn)化相關(guān)的知識(shí)。
?一些用到的
開始之前,先來(lái)復(fù)習(xí)一些會(huì)用到的知識(shí)。
1.地址,字節(jié),位
位(bit)是電子計(jì)算機(jī)中最小的數(shù)據(jù)單位。每一位的狀態(tài)只能是0或1。
字節(jié)(Byte)是用于計(jì)量存儲(chǔ)容量的一種單位,每一個(gè)字節(jié)由8位組成(1Byte = 8bit)。
地址可以理解為在一片內(nèi)存中,每個(gè)字節(jié)(Byte)的編號(hào)。
他們?cè)趦?nèi)存中的關(guān)系可以比作,內(nèi)存是一棟大樓,字節(jié)(Byte)是大樓中的每一層,地址是樓層編號(hào),位(bit)是每一層中的房間,每一層有8個(gè)房間。
?
2.變量的內(nèi)存
編譯器根據(jù)變量的類型,在內(nèi)存中申請(qǐng)一塊空間。例如32位與64位中 int 類型申請(qǐng)到4字節(jié)的空間,可理解為編譯器申請(qǐng)了4層樓,作為”辦公區(qū)域“。
3.指針變量
指針是指程序數(shù)據(jù)在內(nèi)存中的地址。在c語(yǔ)言當(dāng)中,允許用一個(gè)變量來(lái)存放指針,這種變量稱為指針變量。
?
指針變量類型的作用
1 int a; 2 int *p; 3 p = &a; 4 printf("%p %d\n",p,*p);以上程序中,”&“操作符取出了變量 a 在內(nèi)存空間中的首地址,而后通過(guò) “ * ”? 操作符取出首地址所在內(nèi)存空間的數(shù)據(jù)。
前面我們提到,存儲(chǔ)變量的內(nèi)存,是由多個(gè)字節(jié)組成。而指針變量在只知道首地址(第一個(gè)字節(jié)的地址)的情況下,就能找到a的內(nèi)存區(qū)域。它是怎么做到的?先來(lái)看看指針變量的聲明。
我們?cè)诼暶饕粋€(gè)指針變量的時(shí)候,會(huì)根據(jù)它將要指向的變量類型,聲明對(duì)應(yīng)的類型,例如:
1 int a; 2 long b; 3 char c; 4 5 int *pa = &a; 6 long *pb = &b; 7 char *pc = &c;不管是什么類型的指針變量,所存的值都是地址(int類型的值)。那么聲明不同類型的作用是什么?答案是規(guī)定指針在內(nèi)存中每次移動(dòng)的字節(jié)數(shù)。
例如定義“int *pa = &a”,取值時(shí),int類型占4個(gè)字節(jié),指針就從首地址開始移動(dòng),讀取4個(gè)字節(jié)。同理,short類型占2字節(jié),指針就移動(dòng)2字節(jié)。通過(guò)聲明指針類型,告訴指針每次移動(dòng)多少字節(jié),來(lái)獲取變量的值。
?
值相同的兩個(gè)指針?biāo)赶虻淖兞康闹悼梢圆煌?/strong>
“值相同的兩個(gè)指針變量”,意思是兩個(gè)指針變量指向同一個(gè)首地址。但是如果指針變量的類型不同,因?yàn)橹羔樢苿?dòng)的字節(jié)數(shù)量不同,就可能讀取出不同的數(shù)據(jù)。
要實(shí)現(xiàn)不同類型指針變量指向同一個(gè)地址,需要使用指針類型轉(zhuǎn)換。
1 short a = 1; 2 short *p1 = &a; 3 int *p2 = (int *)p1; 4 printf("%d %d",*p1,*p2);以上例子將一個(gè)每次移動(dòng)讀取2字節(jié)的 short 類型指針變量,轉(zhuǎn)化為一個(gè)每次讀取4字節(jié)的int型指針變量。
接下來(lái),我們通過(guò)指針類型轉(zhuǎn)換,用同一個(gè)首地址,取出不同的值。
1 #include <stdio.h>2 int main()3 {4 short c[2]; //等價(jià)于申請(qǐng)2個(gè)連續(xù)的內(nèi)存空間,每個(gè)空間2字節(jié) 5 c[0] = 1; //為第一個(gè)short空間賦值為1 6 c[1] = 1; //為第二個(gè)short空間賦值為1 7 short *p1 = c; //p1指向c[]首地址 8 int *p2 = (int *)p1; //p2指向c[]首地址,并強(qiáng)制轉(zhuǎn)換類型為 int 9 10 printf("p1指向:%p\np2指向:%p\n",p1,p2); 11 printf("p1取出:%d\np2取出:%d\n",*p1,*p2); 12 return 0; 13 } 14對(duì)應(yīng)結(jié)果為:
p1指向:000000000062FE30
p2指向:000000000062FE30
p1取出:1
p2取出:65537
根據(jù)二進(jìn)制轉(zhuǎn)換得,10000000000000001 為 65537。由此可驗(yàn)證強(qiáng)制轉(zhuǎn)換前指針讀取2字節(jié),轉(zhuǎn)化后讀取4字節(jié)。兩個(gè)指針指向的首地址相同,但是讀出了不同的結(jié)果。
?
?
//**************************
內(nèi)存中的地址
地址的本質(zhì)就是一串0和1的機(jī)器代碼,內(nèi)存中的地址沒有明確數(shù)據(jù)類型,但地址值有類型,以32位編譯器為例,內(nèi)存中的地址是一個(gè)32位的整數(shù)。無(wú)論什么類型的指針變量,在內(nèi)存中本質(zhì)上都是一樣的,都是一個(gè)整數(shù)值的地址值,該地址值可以轉(zhuǎn)換為其他類型,比如float或char,但一般不要強(qiáng)轉(zhuǎn),此時(shí)已不再是合法地址而是一個(gè)單純的數(shù)據(jù)值,除了沒有意義外,還會(huì)出現(xiàn)數(shù)據(jù)讀取錯(cuò)誤(后面會(huì)解釋)。
| 1 | int?a; |
當(dāng)我們用a時(shí),由于前面把a(bǔ)定義為int型,則編譯器知道從a的地址開始向后取4個(gè)字節(jié)再把它解釋成int型。
指針變量及不同指針類型的含義
(1)指針變量
?1?int?*a;?
指針變量,本質(zhì)上是一個(gè)變量,只是它是存放地址的變量,指針的類型代表的是它所指向的變量的類型。因此就有了指向整型、字符型、浮點(diǎn)型等其它類型的指針,但實(shí)際上所有類型的指針變量存放的都是int型。
上述代碼表示指向整型的指針變量a,其中a表示一個(gè)地址值,上面曾提到地址沒有明確的數(shù)據(jù)類型,因?yàn)榈刂房梢詾橹赶蛘偷闹羔?#xff0c;可以為指向浮點(diǎn)型的指針。指針類型為整型,表示當(dāng)我們對(duì)該地址進(jìn)行訪問(wèn)(解引用)時(shí),編譯器會(huì)將它解釋為整型。
注意:指針地址只指向數(shù)據(jù)存儲(chǔ)的內(nèi)存的位置,具體變量的類型由編譯器告知。
(2)不同類型的指針
聲明不同類型的指針變量既是規(guī)定了該變量結(jié)合指針運(yùn)算符時(shí)讀取內(nèi)存中的字節(jié)數(shù),同樣規(guī)定了在指針移動(dòng)和指針的運(yùn)算時(shí)(加、減)在內(nèi)存中移動(dòng)的最小字節(jié)數(shù)。
強(qiáng)制轉(zhuǎn)換的原理
(1)普通變量強(qiáng)轉(zhuǎn)
(float)a,就是先按照int類型取出該數(shù)值,再將該數(shù)值按照int to float的規(guī)則轉(zhuǎn)換成float型,如果反過(guò)來(lái),則會(huì)發(fā)生數(shù)據(jù)截?cái)唷?/p>
(2)指針變量強(qiáng)轉(zhuǎn)
舊指針 to 新指針的強(qiáng)制類型轉(zhuǎn)換是指將指針?biāo)傅膬?nèi)容的類型由原先的類型轉(zhuǎn)換為后面的類型:即進(jìn)行變量解釋的時(shí)候,解釋的類型變化。
如果有一個(gè)指針p,我們需要把它的類型和所指向的類型改為TYEP*和TYPE,那么語(yǔ)法格式是:(TYPE*)p;這樣強(qiáng)制類型轉(zhuǎn)換的結(jié)果是一個(gè)新指針,該新指針的類型是TYPE*,它指向的類型是TYPE,(也就是說(shuō),新指針指向的數(shù)據(jù)將會(huì)用TYPE類型進(jìn)行解釋,如果之前是浮點(diǎn)型數(shù)據(jù)-3.75,先將其轉(zhuǎn)換為二進(jìn)制代碼,然后轉(zhuǎn)化為TYPE類型存儲(chǔ)),它指向的地址就是原指針指向的地址。
注意:(int &)y,告訴編譯器將y當(dāng)做int看待
1 void test02(){2 3 //-----------------------------4 //float指針相關(guān)的強(qiáng)制轉(zhuǎn)換5 //不同類型的指針變量,在內(nèi)存中本質(zhì)上都是一樣的,都是一個(gè)整數(shù)值的地址值,一般表現(xiàn)為8位十六進(jìn)制數(shù)6 //不同的類型表示地址指向值的解釋類型,及該變量在內(nèi)存中占據(jù)字節(jié)的數(shù)目7 //地址值可以強(qiáng)轉(zhuǎn)為其他類型,但將地址指向值解釋為其他類型容易出錯(cuò),如float解釋為int8 //常量直接賦值給指針是不可以的,必須強(qiáng)轉(zhuǎn)為合法地址,另外由于內(nèi)存地址是整型值,因此浮點(diǎn)型數(shù)據(jù)不能成為合法地址9 //----------------------------- 10 float a = 10.1; 11 float *p_ = &a; 12 13 cout << typeid(p_).name() << endl; //float* 14 cout << p_ << endl; //地址,8位十六進(jìn)制數(shù)字(32位二進(jìn)制數(shù)字) 15 cout << *p_ << endl; //地址指向的數(shù)據(jù),同樣為32位二進(jìn)制數(shù)字,按照f(shuō)loat進(jìn)行解釋,得到10.1 16 17 //cout << (float)p_ << endl; //不允許將地址直接轉(zhuǎn)化為浮點(diǎn)型,但可以間接修改 18 cout << (char)p_ << endl; //直接將地址轉(zhuǎn)化為字符型 19 cout << (int)p_ << endl; //直接將地址轉(zhuǎn)化為十進(jìn)制整型 20 21 cout << (int*)p_ << endl; //將地址float *類型轉(zhuǎn)換為int*,后面的解釋會(huì)發(fā)生變化,但地址的十六進(jìn)制數(shù)字不會(huì)發(fā)生變化 22 cout << *(int*)p_ << endl; //地址指向的數(shù)據(jù),會(huì)按照int型進(jìn)行解釋,得到1092721050 23 cout << (int&)(*p_) << endl; //同樣的將數(shù)據(jù)解釋為int型,得到1092721050,?則是告訴編譯器將數(shù)據(jù)看成int對(duì)待 24 25 cout << *(int*)&p_ << endl; //將地址(8位十六進(jìn)制數(shù)字)轉(zhuǎn)換為十進(jìn)制,先取p_的地址,然后按照int進(jìn)行解釋 26 cout << *(float*)&p_ << endl; //將地址(8位十六進(jìn)制數(shù)字)轉(zhuǎn)換為float,先取p_的地址,然后按照f(shuō)loat進(jìn)行解釋 27 cout << *(float*)p_ << endl; //輸出10.1 28 29 cout << (char*)p_ << endl; //將地址float*類型轉(zhuǎn)換為char*類型,其實(shí)地址的十六進(jìn)制數(shù)字不會(huì)發(fā)生變化,但<<會(huì)直接輸出指向的字符串 30 cout << static_cast<const void *>(p_) << endl; //可以這樣輸出轉(zhuǎn)換為char*后的地址 31 cout << *(char*)&p_ << endl; //將地址(8位十六進(jìn)制數(shù)字)轉(zhuǎn)換為char,先取p_的地址,然后按照char進(jìn)行解釋 32 cout << *(char*)p_ << endl; //將數(shù)值型數(shù)據(jù)按照字符類型,這里無(wú)法將float解釋為char型,但是int型默認(rèn)可以轉(zhuǎn)換為ASCII碼,解釋為char型 33 }(3)為什么內(nèi)存中的地址值不能直接轉(zhuǎn)換為float?
當(dāng)?shù)刂分缔D(zhuǎn)換為float時(shí),編譯器會(huì)有兩種轉(zhuǎn)換方式:(1)將整型地址值轉(zhuǎn)換為浮點(diǎn)型;(2)將地址指向的值轉(zhuǎn)換為浮點(diǎn)型,但不知道采用哪一種,需要我們顯式地進(jìn)行轉(zhuǎn)換。
(4)地址值強(qiáng)轉(zhuǎn)后訪問(wèn)的數(shù)據(jù)錯(cuò)誤
前面提到可以將地址值轉(zhuǎn)為float或char,此時(shí)地址值變成了數(shù)據(jù)(有可能無(wú)法解釋為char數(shù)據(jù)),而非合法地址。在對(duì)該地址進(jìn)行訪問(wèn)時(shí),需要將該地址值重新變?yōu)楹戏ǖ刂?#xff0c;通過(guò)(int*)強(qiáng)轉(zhuǎn),但浮點(diǎn)型數(shù)據(jù)不允許作為地址值。
(5)字符強(qiáng)轉(zhuǎn)為合法地址
1 void test04(){2 char m = 'b';3 char *n = "b";4 //cout << typeid('b').name() << endl;5 float *p0;6 int *p1;7 float *p2;8 float *p3;9 p0 = (float*)(&m); 10 p1 = (int*)(m); //會(huì)先轉(zhuǎn)換為m變量的ASCII值,98 11 p2 = (float*)(98); 12 p3 = (float*)(n); 13 //cout << &(int(m)) << endl; 14 cout << static_cast<const void *>(&m) << endl; 15 cout << p0 << endl; 16 cout << p1 << endl; 17 //cout << *p1 << endl;//相當(dāng)于將98強(qiáng)轉(zhuǎn)為合法地址,但并沒有對(duì)該地址進(jìn)行初始化,因此直接輸出會(huì)顯示訪問(wèn)沖突 18 cout << p2 << endl; 19 cout << p3 << endl; 20 }特別注意:char*類型不能直接由cout輸出對(duì)應(yīng)地址,cout會(huì)直接輸出指向的字符串,可以用static_cast<const void *>(&m)來(lái)進(jìn)行地址輸出。
總結(jié)
以上是生活随笔為你收集整理的指针类型和指针类型转换的理解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C指针详解(经典,非常详细)
- 下一篇: 超简单:解析 yml 类型(applic