深入理解指针类型间的转换
原博地址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。为了正确存储不同类型的数值,就需要将首地址声明不同类型的地址,来满足存储数据的要求。
综合代码演示:
- #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
小结:无论什么类型的指针变量,在内存中本质上都是一样的,都是一个整数值的地址值;而这个不同的类型仅仅是向系统说明该变量在内存中占据字节的数目,如:字符型的指针变量在内存中占据一个字节、一个整形的指针变量在内存中占据四个字节等等。