深入理解指針類型間的轉換
原博地址http://blog.csdn.net/sszgg2006/article/details/8307331
當我們初始化一個指針或給一個指針賦值時,賦值號(=)的左邊是一個指針,賦值號(=)的右邊是一個指針表達式,在絕大多數情況下,指針的類型和指針表達式的類型是一樣的,指針所指向的類型和指針表達式所指向的類型是一樣的。
例一:
1、 float f=12.3;
2、 float*fptr=&f;
3、 int *p;
在上面的例子中,假如我們想讓指針p指向實數f,應該怎麽搞?是用下面的語句嗎?
p=&f;
不對。因為指針p的類型是int*,它指向的類型是int,而表達式&f的結果是一個指針,指針的類型是float*,它指向的類型是float。兩者不一致,直接賦值的方法是不行的。至少在我的MSVC++6.0上,對指針的賦值語句要求賦值號兩邊的類型一致,所指向的類型也一致,其它的編譯器上我沒試過,大家可以試試。為了實現我們的目的,需要進行"強制類型轉換":p=(int*)&f;
如果有一個指針p,我們需要把它的類型和所指向的類型改為TYEP*和TYPE,那麽語法格式是:(TYPE*)p;這樣強制類型轉換的結果是一個新指針,該新指針的類型是TYPE*,它指向的類型是TYPE,它指向的地址就是原指針指向的地址。而原來的指針p的一切屬性都沒有被修改。
一個函數如果使用了指針作為形參,那麽在函數調用語句的實參和形參的結合過程中,也會發生指針類型的轉換。
例二:
voidfun(char*);
int a=125,b;
fun((char*)&a);
...
...
voidfun(char*s)
{
char c;
c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;
c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;
}
}
註意這是一個32位程序,故int類型占了四個字節,char類型占一個字節。函數fun的作用是把一個整數的四個字節的順序來個顛倒(註意到了嗎?),在函數調用語句中,實參&a的結果是一個指針,它的類型是int *,它指向的類型是int。形參這個指針的類型是char*,它指向的類型是char。這樣,在實參和形參的結合過程中,我們必須進行一次從int*類型 到char*類型的轉換,結合這個例子,我們可以這樣來想象編譯器進行轉換的過程:編譯器先構造一個臨時指針 char*temp,然後執行temp=(char*)&a,最後再把temp的值傳遞給s。所以最後的結果是:s的類型是char*,它指向的類型是char,它指向的地址就是a的首地址。
我們已經知道,指針的值就是指針指向的地址,在32位程序中,指針的值其實是一個32位整數。那可不可以把一個整數當作指針的值直接賦給指針呢?就象下面的語句:
unsigned inta;
TYPE*ptr;//TYPE是int,char或結構類型等等類型。
...
...
a=20345686;
ptr=20345686;//我們的目的是要使指針ptr指向地址20345686(十制
ptr=a;//我們的目的是要使指針ptr指向地址20345686(十進制)
編譯一下吧。結果發現後面兩條語句全是錯的。那麽我們的目的就不能達到了嗎?不,還有辦法:
unsigned inta;
TYPE*ptr;//TYPE是int,char或結構類型等等類型。
...
...
a=某個數,這個數必須代表一個合法的地址;
ptr=(TYPE*)a;//呵呵,這就可以了。
嚴格說來這裏的(TYPE*)和指針類型轉換中的(TYPE*)還不一樣。這裏的(TYPE*)的意思是把無符號整數a的值當作一個地址來看待。
上面強調了a的值必須代表一個合法的地址,否則的話,在你使用ptr的時候,就會出現非法操作錯誤。
想想能不能反過來,把指針指向的地址即指針的值當作一個整數取出來。完全可以。下面的例子演示了把一個指針的值當作一個整數取出來,然後再把這個整數當作一個地址賦給一個指針:
例十六:
int a=123,b;
int*ptr=&a;
char *str;
b=(int)ptr;//把指針ptr的值當作一個整數取出來。
str=(char*)b;//把這個整數的值當作一個地址賦給指針str。
小結:可以把指針的值當作一個整數取出來,也可以把一個整數值當作地址賦給一個指針。
通過以上分析,我們可以思考下:什麽是指針變量?
指針變量,本質上是一個變量,只是它是存放地址的變量,指針的類型代表的是它所指向的變量的類型。因此就有了指向整型、字符型、浮點型等其它類型的指針,但實際上所有類型的指針變量存放的都是int型(對於16位編譯系統,比如 TC,int是2字節,對於32位編譯系統,比如VC,GCC,int是4字節)的地址。因此從本質上講,不同類型的指針變量並沒有區別(因為指針變量的類型 為int型,因此指針變量只能存放地址。註意和指針指向對象的類型區分開),指針變量所存儲的地址為指針所指向的對象的首地址。
#include <stdio.h>
int main(void)
{
int * pint;
char * pchr;
float * pflt;
int a = 2;
char b = 3;
float c = 3.5;
pint = &a;
pchr = &b;
pflt = &c;
printf("pint = 0x%p/n",pint);
printf("pchr = 0x%p/n",pchr);
printf("pflt = 0x%p/n",pflt);
return 0 ;
}
輸出結果:
pint = 0x0012FF3C
pchr = 0x0012FF33
pflt = 0x0012FF24
不同類型的指針變量之間的區別?
我們都知道不同類型的指針變量指向不同類型的對象,這些指針變量結合指針運算符(*)就等價於指向的對象的值,但我們又知道所有的指針變量的類型都是一樣的(都是int型)。到底聲明不同類型的指針變量的背後是什麽?其實聲明不同類型的指針變量既是規定了該變量結合指針運算符時讀取內存中的字節數,同樣在指針移動和指針的運算時(加、減)在內存中移動的最小字節數。
指針變量強制類型轉換轉換的背後
首先看個例子
#include <stdio.h>
int main(void)
{
int * pint;
char * pchr;
float * pflt;
int a = 2;
char b = 3;
float c = 3.5;
pint = &a;
pchr = &b;
pflt = &c;
printf("pint = 0x%p/n",pint);
printf("pchr = 0x%p/n",pchr);
printf("pflt = 0x%p/n",pflt);
//以上三句等同於下面三句吧?
printf("pint = 0x%p\n",&pint);
printf("pchr = 0x%p\n",&pchr);
printf("pflt = 0x%p\n",&pflt);
*(int *)0x0012FF3C =256; //a的首地址為0x0012FF3C
printf("a = %d/n",a);
pchr = (char *)pint;
printf("*(int *)0x0012FF3C = %d/n",*pchr);
printf("*(int *)0x0012FF3D = %d/n",*(pchr + 1));
printf("*(int *)0x0012FF3E = %d/n",*(pchr + 2));
printf("*(int *)0x0012FF3F = %d/n",*(pchr + 3));
*(int *)0x0012FF3C = 125;
printf("a = %d/n",a);
pchr = (char *)0x0012FF3C;
printf("*(int *)0x0012FF3C = %d/n",*pchr);
printf("*(int *)0x0012FF3D = %d/n",*(pchr + 1));
printf("*(int *)0x0012FF3E = %d/n",*(pchr + 2));
printf("*(int *)0x0012FF3F = %d/n",*(pchr + 3));
return 0 ;
}
輸出結果:
pint = 0x0012FF3C
pchr = 0x0012FF33
pflt = 0x0012FF24
a =256
*(int *)0x0012FF3C = 0
*(int *)0x0012FF3D = 1
*(int *)0x0012FF3E = 0
*(int *)0x0012FF3F = 0
a = 125
*(int *)0x0012FF3C = 125
*(int *)0x0012FF3D = 0
*(int *)0x0012FF3E = 0
*(int *)0x0012FF3F = 0
由於 pchr ,pint是不同類型的指針變量,因此在pchr = (char *)pint;語句中需要進行強制類型轉換轉換。其實在此可以不理解成指針的強制類型轉換(因為pint還是指向int型的指針,自身並沒有任何改變),暫時可以將pint 理解成存儲a變量的首地址的指針變量,這樣就和pchr = (char *)0x0012FF3C語句等價,但由於pchr的聲明為字符型的指針變量,因此等號的另一端應是一個字符型的地址,所以就將0x0012FF3C聲明(強制轉化)為字符型的地址。但實際上地址本身就是地址,沒有什麽字符型、整型等區別的,只是為了方便說明,從理論上說的通,就認為是字符型的吧(個人是這樣理解的)。同樣*(int *)0x0012FF3C= 256 可以理解為在內存中用一個int型變量的大小的字節數來存儲256這個值,首地址為0x0012FF3C。為了正確存儲不同類型的數值,就需要將首地址聲明不同類型的地址,來滿足存儲數據的要求。
綜合代碼演示:
[cpp] view plain copy print?
- #include "stdafx.h"
- #include "iostream"
- using namespace std;
- class CA
- {
- public:
- char c1;
- char c2;
- private:
- };
- class CB
- {
- public:
- int i1;
- int i2;
- CB():i1(1){}
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- cout<<"測試一:"<<endl;
- CA objectA;
- objectA.c1=‘a‘;
- objectA.c2=‘b‘;
- CA *pA=&objectA;
- CB *pB=(CB*)pA;
- printf("pA=0x%p,&pA=%p\n",pA,&pA);
- cout<<pB->i1<<endl;
- printf("pB=0x%p,&pA=%p\n",pB,&pB);
- //cout<<pB->c1<<endl;
- cout<<"測試二:"<<endl;
- int * pint;
- char * pchr;
- float * pflt;
- int a = 2;
- char b = 3;
- float c = 3.5;
- pint = &a;
- pchr = &b;
- pflt = &c;
- printf("pint = 0x%p\n",pint);//等同於printf("&a=0x%p\n",&a);輸出的是指針變量中存儲的變量的地址,即變量a的地址
- printf("pchr = 0x%p\n",pchr);
- printf("pflt = 0x%p\n",pflt);
- //註意同以下三句的區別,即:指針變量與&指針變量的區別
- printf("pint = 0x%p\n",&pint);//輸出的是指針變量自身的地址
- printf("pchr = 0x%p\n",&pchr);
- printf("pflt = 0x%p\n",&pflt);
- cout<<"測試三:"<<endl;
- //*(int *)0x0012FF3C = 256;//a的首地址為0x0012FF3C
- //printf("a = %d/n",a);
- //pchr = (char *)pint;
- //printf("*(int *)0x0012FF3C = %d/n",*pchr);
- //printf("*(int *)0x0012FF3D = %d/n",*(pchr + 1));
- //printf("*(int *)0x0012FF3E = %d/n",*(pchr + 2));
- //printf("*(int *)0x0012FF3F = %d/n",*(pchr + 3));
- //*(int *)0x0012FF3C = 125;
- //printf("a = %d/n",a);
- //pchr = (char *)0x0012FF3C;
- //printf("*(int *)0x0012FF3C = %d/n",*pchr);
- //printf("*(int *)0x0012FF3D = %d/n",*(pchr + 1));
- //printf("*(int *)0x0012FF3E = %d/n",*(pchr + 2));
- //printf("*(int *)0x0012FF3F = %d/n",*(pchr + 3));
- system("pause");
- return 0;
- }
參考文獻:http://blog.csdn.net/jinlei2009/article/details/5719325和http://blog.csdn.net/huangtonggao/article/details/6233623
小結:無論什麽類型的指針變量,在內存中本質上都是一樣的,都是一個整數值的地址值;而這個不同的類型僅僅是向系統說明該變量在內存中占據字節的數目,如:字符型的指針變量在內存中占據一個字節、一個整形的指針變量在內存中占據四個字節等等。
深入理解指針類型間的轉換