原文地址:http://hi.baidu.com/sunkanghome/blog/item/3cae5e0379d8417e3912bbfa.html
1、强制类型转换
看一下这个c代码:
int a = 0x123456; //0x是十六进制的表示法
char c = (char)a;
printf("%x/n", c);
在我的编译环境里输出的是什么呢? 是56,怎么得来的呢?
int a的内存如下(左边是低地址的内容,右边是高地址的内容):0x56 0x34 0x12 0x00
"是不是搞反了?" 有人奇怪(正如我当年~~~~~~),这个问题还得详细讨论,但不是在这里,是在别的文章,上google搜一下“little endian ”,看一下相关的知识,然后再回来看啊。(我用的是intel x86系列的cpu,所以是little endian 序的)
(little endian序的内容就是:低字节数据放入低地址,高字节数据放入高地址,比如上面的56是低字节,所以放入低地址 )
现在转换到char,char在我的环境里占一字节,(char)a的意思就是,取a中的第一个字节 出来,就是0x56了。
这就是c语言中基本类型的强制类型转换。
(不知道上面的方法给了你什么启发,比如下面的问题:编写一个c程序,判断你用的机子是little endian还是big endian,你想到思路了吗?呵呵)
那么,如果是自定义类型的类型转换会如何呢?
比如:
struct A
{
int a;
};
struct B
{
char c;
};
struct A aA;
aA.a = 0x123456;
printf( "%d/n", ( (struct B)aA ).c);
很遗憾,这个是通不过编译的,为什么呢?
原因可能如下(是我猜测的,如有不正确之处,各位指点一下,谢谢):
基本类型是编译器定义的,所以编译器里面也实现了关于基本类型的转换函数,比如(int)a,实际上是在编译期间,在编译器的内部调用了相关的转换函数,所以就编译通过。
但是我们自定义的类型,编译器里面没有相关的转换函数,所以就转换不了。
为了弥补这个缺陷,c++中提供了一种转换运算符的重载,只要重载转换运算符(),就可以实现自定义类型之间的类型转换了。(关于转换运算符重载的用法,去google吧,我以前也没用过,只是知道有这个东西而已,呜呜~)
2、关于指针的类型转换的含义
那么,到底有没有办法能实现以上的功能呢?能~!用指针类型转换就能!
下面还是从基本类型开始:
int a = 0x123456;
int *pa = &a;
char *ca = (char *)pa;
printf("%x/n", *ca);
答案呢?还是56,呵呵~(原理和上面的一样,这里就不重复了)
所以我个人怀疑,基本类型的运算符的强制转换操作在编译器内部还是通过指针来实现的(个人猜测,如有不正确之处,请多多指教,谢谢)
所有的指针变量的大小都是固定的,都是4字节 (WinX86)。
那么,指针变量定义时候,前面的类型说明什么呢?既然 int *pInt; 和char *pChar;中,pInt和pChar的大小都是4字节,那么,int和char在这里的作用又是干嘛的 ?
指针定义的时候,前面的类型声明,说明的是 指针所指向变量。具体一点,是所指向的变量在内存的大小(sizeof()可以算出来),进一步可以推算出,该指针变量一次操作读取的内存单位 。
比如
int a = 0x123456;
int *pInt = &a;
那个int *就说明了pInt指向的类型是一个整形,这意味着:pInt每次操作时候,以4字节为单位,就是说每次4字节4字节的取内存;所以pInt指向的是a的 地址(里面从低到高放着:0x56, 0x34,0x12, 0x00...),读取4字节,再注意到这是little endian序,*pInt就是0x00123456了。
如果是 short *pShort = (short*)&a;呢?
(shrot *)&a说明了,取a的地址,放到pShort变量中去,而pShort的类型声明是short,也就是说每次是以内存的2字节为单位,是2字节2字节的取数据,每次pShort+1其实是在内存里面向高处移动2字节。
*pShort = 多少呢? 是0x3456(过程和上面的相似,这里不重复了)
那么对于自定义类型的指针转换可以嘛?下面试试例子吧。
struct A
{
int a;
};
struct B
{
char c;
};
struct A aA;
struct B *pB;
aA.a = 0x123456;
pB = (struct B*)&aA;
printf("%x/n", pB->c);
编译通过...连接通过...执行结果是56,Oh,Yeah!大功告成!
转换的过程这里就不重复了。
所以说一般的类型转换是有限的,但指针的类型转换却是无限的~(你可以这样称呼它:黑客的宠儿~~)
本笔记其实就说了一件事,指针定义的类型说明的作用 就是:该变量指向的内存空间的大小和一次操作的单位 。
本文来自CSDN博客,转载请标明出处: http://blog.csdn.net/jiangnanyouzi/archive/2009/01/12/3759144.aspx
===============================================================
先看下面的例子
#include <stdio.h>
int main(void){
int *ip;
int a[2]={0x301,0x44444444};
ip = a;
printf("*ip=%x, *(ip+1)=%x/n" , *ip , *(ip+1));
printf("*(char *)ip=%x, *((char *)ip+1))=%x" , *(char *)ip , *((char *)ip+1 ) );
return 0;
}
运行后的结果如下
其实放在内存的内容都是二进制编码,只是拿出来的类型依照指针类型而定 ,更确切的应该说成只是在处理内存时是依照指针类型而定,比如用(char *)作强制转换,则将来处理相应的内存地址时是按字节为单位处理 的,如果用(int *)则将按4个字节为单位进行处理 ,它们的差异会是巨大的。
现在我们来分析上面的结果
1.int *ip 按四个字节 取值 ,由于a是int数组,所以每个元素都是int型的,前四个字节是Ox0301,后四个字节是0x44444444
2.char*ip按一个字节 取值 ,a的分析同上,由于0x代表16进制,所以0x0301每个数字占用4位,以个字节是8位,所以头一个字节是低位01,第二个字节是高位03
*((char *)ip+1) ,*((char *)ip)就使用了强制类型转换
本文来自CSDN博客,转载请标明出处: http://blog.csdn.net/lemon_fantasy/archive/2008/09/27/2988180.aspx
====================================================================
指针是什么呢?一句话,指针其实就是地址。只要有了一个地址,就可以访问到特定的内存。这就是指针。指针其实和刀差 不多,同样是刀,可以切菜,可以杀牛,可以做手术,可以杀人—— 慢!大家有没有想到?做手术的刀和切菜杀牛乱七八糟的刀其实都是不同的。虽然都是刀,但有这样那样的区别!对,指针也是这样,因为用途不同(可能指向一个 int啊,也可能指向一个double啊),所以指针也分很多种。这些指针本质上都是一样的(都是内存的地址嘛),如果指向一个地方,那么值也是一样的 (同一个地方的地址自然只有一个咯)。问题来了。刀还可以从形状成色看出来是干什么的,但单纯一个指针,你怎么看出来它指着的是什么东西?除了从声明时候 的类型看出以外,具体的使用时,不同类型的指针之间到底会有什么差别呢?
我们来做一个实验。这里需要大家理解类型转换。看过上面对类型转换的介绍,
理解这个实验应该不难。
CODE:
int a[4]={1,2,3,4};
char *pt=(char*)&a;
printf("%d %d",*(a+1),*(int*)(pt+4))
大家可以运行一下这段程序,看看输出多少。如果大家没有输错的话,结果应该是2,2。
我们来分析一下这个程序。首先是声明了一个有4个元素的int数组。数组名其实就是一个指针。大家可以把它当作指向 int型变量的常指针(所谓的常指针,就是值不会变的指针。你想,你怎么改变一个数组名指向元素的位置呢?),我们可以把数组名的类型写成int *const。这里const在星号的后面。所以标示指针本身是不能被改变的,而指针所指向的元素可以改变
这样第二行就好理解了。将数组a的首地址强制类型转换成char*,然后赋给pt这个char*类型的指针 。这里的转换是int*const => char*。为什么可以这么转换呢?上面已经跟大家说了,不同类型的指针实质上是一样的(都是长度为4个字节的整型数(地址值),强制类型转换并不会改变指针本身的值 )。请大家务必记住这句话。这表明,所有的指针之间都是可以转换的。当然,必须用强制类型转换。因为指针的类型毕竟不同 。然后注意的是,所有指针都可以转换,但是不是所有的指针都可以被赋值,你如果写了a=(int*const)pt;自然是会出错的了。
假设这个数组a在内存的0x4000(注意:目前大多数计算机,指针都是32位,严格说来这里应该是 0x00004000,但是为了简便我只写16位出来,大家看到以后不要感到困惑)处,我们来看看现在的内存到底是什么样子的:(至于为什么是这样,可以 看我的另一篇文章《对函数调用的深入探讨》)
CODE:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
-------------------------------------------------------
0x3FF0: XX XX XX XX XX XX XX XX XX XX XX XX 00 40 00 00
0x4000: 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
这里,0x3FFC位置的,就是pt了。注意到了吗?虽然pt指向的是char型的变量,但是pt仍然是四个字节长,因为是地址嘛。希 望看到了内存布局,你能更加理解“指针和指针之间实质上是一样”这句话。看,x3FFC开始的四个字节的值是0x00004000(注意:X86的计算 机,字节是倒着摆的,所以我们要正过来看)。正好是数组首元素所在的位置。
然后我们看*(a+1),a的类型是int *const,a+1又是什么呢?C规定,将一个指针加上一个数字,那么这个指针的值会递增这个数字乘以指针指向元素的大小。晕了么?解释一下你就明白了。
int型是四个字节。如果你给指针加一,指针变成了0x4001的话,那int型不是被破坏了么?因为如果你在这个 地址写一个int的话,那么肯定有三个字节写到了第一个元素里,一个字节写到了第二个元素里,估计离程序崩溃也就不远了。所以这里一次加了 1*sizeof(int)=1*4=4个字节。1代表加的数字,而4就是int的长度了。OK,现在和谐了,a+1的值为0x4004 ,从图书可以很清晰地看出,0x4004是数组第二个元素的首地址。 于是我们将0x4004处的值用地址操作符(*)取出来,交给printf输出,我们就得到了第一个值2。
再来看*(int*)(pt+4 ) 。pt是一char型的指针。现在值仍然是0x4000,因为char型大小为一个字节,所以pt+4其实就是直接相加,其值为0x4004。但是pt是指向char的指针啊,就算加了4,其结果仍然是指向char型的指针 。这个时候如果取数的话,只能取到一位,这不行啊!于是,我们使用强制类型转换 ,将计算结果0x4004强制转换成了指向int型的指针 (注意指针本身的值并没有改变,仍然是0x4004 ),再取出来,这里的转换是char* => int*。OK,仍然得到了2这个值。
我们来总结一下:
1 指针的内容是个32位的值。(位数是由计算机定的,目前大部分为32位)
2 不同类型指针之间的差别在于,给指针一次递增或者递减的“跨度”不 同,以及使用地址操作符取出的元素的长度不同。
3 对指针进行类型转换,指针本身的值不改变 。参照第二点可以知道,对指针进行类型转换,只会改变指针递增递减时的跨度,和使用地址操作符取出元素的长度。
请大家牢牢记住这三点,这样无论看到什么应用,脑子里都可以清清楚楚的。