您的位置:首页 > 其它

C---int和指针转换注意事项

2016-05-17 20:28 239 查看
这个标题也许写的有点不知所以然了,其实博主今天要讨论的自己在复习c/c++指针和引用的时候遇到的一些问题,慢慢说来…..

开始的时候写了这么一段代码:

int a = 1;
int* p = &a;

printf("指针变量p的值 p - 16表示  是 = %p \n",p);
printf("a的地址 p - 16表示  是 = %p \n",&a);

/*输出
指针变量p的值 p - 16表示  是 = 0x7fff375272a0
a的地址 p - 16表示  是 = 0x7fff375272a0
*/


输出的结果是一样的,也就是说明指针变量p的值 == a的地址

之后我继续写了一段:

printf("指针变量p的值 x - 16表示  是 = %x\n",p);

/*输出
指针变量p的值 x - 16表示  是 = 375272a0
*/


%x是输出一个数的十六进制格式表示,我们可以看出来得到的输出有点相像,只不过%p的输出多了前面一部分,0x是十六进制的标志,后面的f表示16进制中的15,多次输出发现只有后8位是一直没变的,前面的4位总是变化(确切的说是第四位)。

它变化的范围最大是7fff(111111111111111),15位,也就是还剩下17位是没变的。这里我猜的是它的编码规则变了,当后面的32位改变的时候,前面的4位中最后的那一位会根据后32位的大小作相应改变,具体是什么我也不知道。也很有可能我这个想法是错误的,还请知道的朋友指正出来,感激不尽。

那无论我们猜的对不对,有一点是对的,我们不能像32位中一样随随便便将int 和指针类型改变了,比如如下代码

int a = 1;
int p = &a;

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

/*
在64位下输出错误
32位下输出a的值 1
*/


因为64位中,我们若强制将一个32位的变量转换成64位,不仅仅是在前面加一段32bit的数据,而是像我上面想的那样改变了编码规则。这样一来就没法根据int*的地址值来寻址了,因为这个地址的值已经不是原来的32位值了,它被改变了。为了证明这一点,我用32位gcc编译了同一段代码。

int a = 0;
int* p = &a;
int p1 = &a;

printf("p1的值(p - 16)是 %p\n",p1);
printf("p1的值(x - 16)是 %x\n",p1);

printf("指针变量p的值 p - 16表示  是 = %p\n",p);

printf("p1的值转换成void*型(p - 16)是 %p\n",(void*)p1);
printf("p1的值转换成int *型(p - 16)是 %p\n",(int*)p1);

/*32位输出
p1的值(p - 16)是 0xffa60b38
p1的值(x - 16)是 ffa60b38
指针变量p的值 p - 16表示  是 = 0xffa60b38
p1的值转换成void*型(p - 16)是 0xffa60b38
p1的值转换成int *型(p - 16)是 0xffa60b38
*/

/*64位输出
p1的值(p - 16)是 0xa33e0530
p1的值(x - 16)是 a33e0530
指针变量p的值 p - 16表示  是 = 0x7ffea33e0530
p1的值转换成void*型(p - 16)是 0xffffffffa33e0530
p1的值转换成int *型(p - 16)是 0xffffffffa33e0530
*/


这里32位下得到的结果int*型和int型是完全一样的。排除编译器限制严谨程度,int和指针变量类型是可以互相转换的。但是最好别这么做,在这里这么做只是为了了解指针的实质。

64位下就大不相同了,同一个地址的%p是使用64位的编码规则输出一个32位地址,前面的8位变了。若是直接强制地址转换就会将前32位都填充1,这和下面的visual studio恰好相反。

之后在win7下用visual studio,x_64编译执行了一遍,可以对比一下两者



这里可以看出来,在visual studio x64平台上编译得到的结果是不同于gcc 64位的,就指针地址而言,gcc上面的变化是编译器自定的规则,我们无从得知,在vs下可以看出来是直接在前面补上了32位的0,也就是说有效值是完全一样的。

那我们可以知道为什么不能直接对64位平台下的int型转换成指针型来寻址了,因为很可能就是指针的有效值改变了,那寻址就完全失败了,有一行这样的代码都能直接导致整个程序的崩溃。

而在32位下是没更多空间去改变值,大家都是4个字节空间,编码规则一样,所以32位平台下是可以将指针和int型互相转换的,一般不会产生危险(像vs中32位平台,它编译器不允许你将一个指针型赋给一个int型)

综合以上所有:

%x的作用仅仅是将一个整数以32位无符号十六进制输出,它是int型的输出,在32位或64位平台都是4个字节

%p可以看做是输出的指针地址的(值+标识),可能不同情况下是这个标识(让系统识别这是个指针)不一样。

如果是值方面,32位下是一样的,同时强制转换没多余的空间改变值,也就都是一样的。在这种情况下,单对值进行指针类型转换再取地址是可以的,
例如int p = &a; *((int*)p)这个就是将int型p转成int*型,值本身一样,加上标识,再取地址,就是a的值了。

64的话就是分配地址的时候就不仅仅是后32位有效值了,它根据不同的规则(有可能只是填充0,这种情况有效值不变)来重定义一个32位值,所以
大体上是值变了,再加上强制转换类型也都是有自己的规则,填充0或者1,这两者都导致不能直接将32位的整形转换成64位指针型。


为了保证程序的安全性和稳定性,最好不要写这样仅仅针对某个平台可以安全转换类型的代码,应该考虑到多个平台,或者说尽量不使用这样的代码。

总结:

在使用到强制类型转换的时候需要多注意转换的实质

有什么错误还请指出,谢谢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: