C++指针
引言:运行下列代码。
#include<iostream> #include<cstdio> using namespace std; int main() { int t=7; int *a;//这显然是定义了一个指针,int* a等价于int *a a=new int; a=&t; printf("%d ",*a); printf("%p",a); delete a; return 0;
}
运行结果,在我的电脑上是7 0023FF18。放着问题不说,先讲结论。
我们要研究指针,需要知道四个内容:指针的类型,指针指向的变量的类型,指针的值,指针本身占据的内存区域(首先我们要知道指针是“指”向一个变量的变量)。
1. 指针的类型:定义的形式在程序段中写了。我们可以把指针的名称去掉,看剩余部分。下面是几个例子。
int *p; int *()表示我们定义了一个指向整形的指针变量。这是一个相当单纯的指针类型,那么对于更复杂的指针类型,我们如何分析?
int *p[3]; 首先p与“[]”结合,表明p是一个数组(“[]”的优先级比“*”高)。再与“*”结合,表明p里面的元素是指针。最后与int结合,表明p里面的指针元素是指向整形的。
int (*p)(int); p先与“*”结合,表明p是一个指针,再与“()”结合,说明这是一个函数,且该函数有一个整形参数。再与最外面的int结合,说明该函数的返回值为整形。因此p是一个指向一个有一个整形参数且返回值为整形的函数的指针。
2.指针指向的变量的类型:只需把指针的名称和其左边的“*”去掉,剩下的部分就是指针指向的变量的类型。
3.指针的值:在这里我终于要填坑了。之前我们有这样一句话:a=new int; 这句话的意思是为指针a开辟内存空间。我们知道计算机存储数据都是需要空间的(这里俺也知道的不多,就不细讲了……)同理,我们程序中的变量也有自己的空间。计算机访问变量是通过访问变量所在的地址实现的。指针的“值”,也就是指针指向的变量的地址。后面我们还有一句话:delete a; 这句话的意思是释放指针a 的内存空间,也就是说这一步之后指针a就没有内容了。还有,之前输出了一个指针的地址。如果你多试几次,你会发现那个值是固定的,而且就算你关闭C++再重启也是一样,但如果你关机重启,它就会是另一个固定的值了。
我们都知道,赋值号左右的部分必须是类型相同的量。那么这一句话:a=&t; “&”的作用就呼之欲出了,没错,“&t”的作用就是返回t的地址。你可能会问,为什么使用指针a的时候不带“*”号?那是因为“*”的作用是表明a是一个指针,当我们用的时候只要用指针的名称就行了。就好像定义常量的时候我们会写:const int a=7; 但后面用的时候我并不必带上“const”。在回想一下其余的部分。我们经常见到的使用“&”的时候是使用“scanf”时,现在我们知道了,是因为“scanf”是需要用到地址。但指针本来就是一个地址,所以,如果想读入指针,直接读就好了。Code如下:
#include<iostream> #include<cstdio> using namespace std; int main() {
int *a=new int; scanf("%d",a); printf("%d",*a); return 0; }
4.指针自己所占的内存:指针指向某个变量在内存中的地址,它自己也肯定占一定的空间。指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32位平台里,指针本身占据了4个字节的长度。
5.指针的算术运算
如果你给一个指针加上或减去一个数n(咱只知道整数),那么相当于加上或减去n*(sizeof(指针指向的变量的类型))。在数组中比较常用。
Code:
int a[20]; int *p=a;//我略去了开空间的步骤,大家别忘啊! ... //此处略去为整型数组赋值的代码。 ... for (i=1; i<=20; i++) { (*p)++; p++; }
上述代码可以用来给整个数组中的元素加1。是不是有点danteng……不要在意这些细节啦。
6.指针与数组名
数组名其实也就是一个指针。所以我们可以这样:
value=a[0];//也可写成:value=*a;
value=a[3];//也可写成:value=*(a+3);
value=a[4];//也可写成:value=*(a+4);
值得注意的是数组名有双重含义,所以a++之类的操作是不行的。
7.指针与结构类型
我们看下面的代码:
struct node { int a,b,c; }; node ss={20,30,40};//声明了结构对象ss,并把ss的三个成员初始化为20,30和40。 node *p1=&ss;//声明了一个指向结构对象ss的指针。它的类型是node*,它指向的类型是node。 int *p2=(int*)&ss;//声明了一个指向结构对象ss的指针。但是它的类型和它指向的类型和ptr是不同的。
我们怎么用指针p1和p2访问ss的三个成员呢?
p1->a; p1->b; p1->c; *p2;//访问了ss的成员a。 *(p2+1);//访问了ss的成员b。 *(p2+2)//访问了ss的成员c。
但其实p2的方法是不规范的。为什么呢?
所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的存储区里,单元和单元之间没有空隙。但在存放结构对象的各个成员时,在某种编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两个成员之间加若干个“填充字节”,这就导致各个成员之间可能会有若干个字节的空隙。如果你直接访问的话,那么你就有很大的可能会访问到填充字节。