4.1 指针与内存
首先,指针是一种变量,这个变量存储的内容是一个地址,每一个变量在内存中都会占用一定的空间,指针变量也不例外,变量可以在其所在的空间内存储数据。每个变量在内存中都会有一个地址,来标记变量在内存空间中的位置(这里假定环境是在32位系统下)。
下面我们来定义一个int*指针,以及一个int变量,int变量的值为100,int*指针指向int变量:
int* p; int a = 100; p = &a;
从图4-1中可以看到这两个变量在内存中的位置,每个变量都是占4个字节,第一个是指针变量,变量中存储的内容是0x04这个值,也就是a的地址,而a存储的内容则是100这个数值。
图4-1 指针在内存中的表现
从内存的角度看,指针实际上和一个int没有太大差别,本质上都是在内存中记录数据。但从语法上看,指针可以进行更加丰富的操作(如解引用,->操作)。
在游戏中创建的任何对象都是放在内存中的,除了可以用变量名来操作它们,还可以用指针来操作,通过将指针指向想要操作的内存,就可以对这块内存进行操作(释放、读取、修改),如果操作的是一块不可被操作的内存,那么程序就会崩溃。
//char类型变量c的值为'a' char c = 'a'; //指针pc指向c的地址 char* pc = &c; //将指针pc指向的内容修改为'b' *pc = 'b'; //打印出的内容为 b printf("%c", c);
一个指针包含指针的类型,指针指向对象的类型,指针指向对象的内存区,以及指针所占的内存区。代码中的pc指针是一个char*类型的指针,指向一个char类型的对象,其指向的内存区是变量c所在的内存地址,而pc指针本身占用一个4字节大小的空间,任何指针,本质都是存储地址,不因指针类型而改变。
指针的类型限定了这个指针可以做的操作,以及这个指针做这个操作的效果。指针指向对象的类型本身没有重大意义,因为指针实际上可以指向任何对象,不同类型的对象只要做一个强制转换即可,将一个类型指针转换为另外一个类型的指针时,需要谨慎。
指针指向对象的内存区是指向对象在内存中的偏移地址,在这块地址存放了这块对象的内容,是可以操作的内存区,这需要小心操作,一旦操作错误,修改了相临的内存区,很可能出现意外的错误。
指针所占的内存区,是一块32位的内存,相当于一个int,这块内存存放了指向对象的地址,当把指针指向另外一个内存时,相当于修改了这个int的值。
将char* pc=&c修改为下面两行代码,编译运行后,结果是一样的。
int* i = reinterpret_cast<int*>(&c); char* pc = reinterpret_cast<char*>(i);
接下来给大家带来一个非常经典的问题,const 指针和指向const对象的指针,以及指向const对象的const指针,听起来像是绕口令,但面试的时候这道题还是经常出现的。
const char* p1; char* const p2; const char* const p3;
*号前面的是指针的类型,*号后面是对指针的修饰,指向const char*的指针内容不能被修改,而const指针一旦指向某块内存,就不能重新指向另外一块内存,就相当于const int的值不能变。
再来一个问题,下面两个指针,哪个是const指针?哪个是指向const对象的指针?可以看到,*号后面没有带任何修饰符,所以这两个都是指向const对象的指针。碰到类似这样的问题,只需要根据*所在的位置来判断即可。
const char* p1; char const* p2;
在64位的操作系统下,int占64位,8个字节,但为什么sizeof(int)打印出来的数据会是4个字节呢?因为编译器生成的是32位的应用程序,而64位操作系统可以兼容32位应用程序。如果希望打印出来的是8个字节,只需要让编译器编译64位的程序即可,在VS下,可以在解决方案的平台设置中,设置目标平台为64位。