地址的类型转换

功能

对变量的值进行强制类型转换,是把值按照另外一种类型进行存储后读取,变量在内存中的存储形式发生变化.

对变量的地址进行强制类型转换,是变量在内存中的存储形式未发生变化,而在变量读取时读取的方式发生变化。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include<iostream>
using namespace std;
int main(void) 
{ 
    int ch; 
    ch=getchar();
    cout<<ch<<endl;
    cout<<*(char*)(&ch)<<endl;
    cout<<(static_cast<char>(ch))<<endl;
    cout<<(char)ch<<endl;
    return 0; 
}

分析:

*(char*)(&ch);

相当于

char * tp=&ch;
char & t =*tp;

该语句同时作用于地址和值,还做了类型转换。是一个左值表达式。

(static_cast<char>(ch)),
(char)ch 

这两个只是对 ch的值,做类型转换,它们都是右值,不能出现在内建赋值操作符的左边。

补充

左值:变量有地址,(char)&a是把int型变量a当成char型变量使用

右值:数值没有地址,static_cast(a)和(char)a是把int型变量a的数值取出来转换为char型数值。

注意

通过地址的类型转换来改变值,是不可移植的垃圾代码。结果依赖于内存布局,例如不同的大小端结构会导致不同的结果。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <stdio.h>  
int main()  
{  
    int a = 3;  
    float b;  
    float c;  
    b = (float)a;  
    c = *((float *)&a);        
    printf("b = %f\nc = %f\n",b,c);  
    return 0;  
}  

结果

地址的数学计算

指针的数学运算需要考虑指针所代表的变量的大小。指针变量与一个整数相加减并不是用指针变量里的地址直接加减这个整数。这个整数的单位不是byte 而是元素的个数。

以一道题为例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
struct BBB  
{  
long num  
char *name;  
short int data;  
char ha;  
short ba[5];  
}*p;  
p=0x1000000;  
p+0x200=____;  
(Ulong)p+0x200=____;  
(char*)p+0x200=____;

解析:

假设在32位CPU上,

sizeof(long) = 4 bytes
sizeof(char *) = 4 bytes
sizeof(short int) = sizeof(short) = 2 bytes
sizeof(char) = 1 bytes

由于是4字节对齐,

sizeof(struct BBB) = sizeof(p) = 4 + 4 + 2 + 1 + 1/补齐/ + 2*5 + 2/补齐/ = 24 bytes (经Dev-C++验证)

p=0x1000000;

p+0x200=__; = 0x1000000 + 0x200*24

指针加法,加出来的是指针类型的字节长度的整倍数。就是p偏移sizeof(p) 0x200.

(char)p+0x200=__; = 0x1000000 + 0x200*1

结果类型是char,与上面的问题没有任何不同,只是sizeof(p)发生改变,这儿的1是char的数据类型是1字节

(Ulong)p+0x200=__; = 0x1000000 + 0x200

这里涉及到强制转换,将指针变量p 保存的值强制转换成无符号的长整型数。任何数值一旦被强制转换,其类型就改变了。所以这个表达式其实就是一个无符号的长整型数加上另一个整数。

再看一例:

在x86 系统下,其值为多少?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
intmain()
{
int a[4]={1,2,3,4};
int *ptr1=(int *)(&a+1);
int *ptr2=(int *)((int)a+1);
int ptr0=*(a+1)
printf("%d\n",ptr0);
printf("%x,%x",ptr1[-1],*ptr2);
return 0;
}

下面就来分析分析这个问题:

ptr0:a与&a的地址是一样的,但意思不一样。a是数组首地址,也就是a[0]的地址;&a是对象(数组)首地址,a+1是数组下一元素的地址,即a[1];而&a+1是下一个对象的地址,即a[5]。

ptr1:将&a+1 的值强制转换成int类型,赋值给int 类型的变量ptr,ptr1 肯定指到数组a 的下一个int 类型数据了。ptr1[-1]被解析成*(ptr1-1),即ptr1 往后退4 个byte。所以其值为0x4。

ptr2:按照上面的讲解,(int)a+1 的值是元素a[0]的第二个字节的地址。然后把这个地址强制转换成int类型的值赋给ptr2,也就是说ptr2 的值应该为元素a[0]的第二个字节开始的连续4 个byte 的内容。

从上例可以发现,在C++中,数组的下标并非不可以为负数,当数组下标为负数时,编译可以通过,而且也可以得到正确的结果,只是它表示的意思却是从当前地址向前寻址,即为当前地址减去sizeof (类型)的地址值。

指针数组与数组指针的使用: