C++中的指针
指正持有另一个对象的地址,使我们能够间接地操作这个对象,指针的典型用法是构建一个链接的数据结构,例如树(tree),和链表(list),并管理再程序执行过程中动态分配的对象,以及座位函数参数类型,主要用来传递数组或大型的类对象.
每个指针都有一个相关的类型,不同数据类型的指针之间的区别不是在指针的表示上,也不在指针所持有的值上-----对所有类型的指针这两方面都是相同的.
不同之处在于指针所指的对象的类型上,指针的类型可以指示编译器怎样解释特定地址上内存的内容,以及该内存区域应该跨越多少内存单元.
1,如果一个int型的指针寻址到1000内存处,那么在32位机器上,跨越的地址空间是1000-1003.
2,如果一个double型的指针寻址到1000内存处,那么在32位机器上,跨越的地址空间是1000-1007.
下面是指针定义的例子:
int*ip1,*ip2;
complex<double>*cp;
string*pstring;
vector<int>*pvec;
double*dp;
我们通过在标示符前加一个解引用操作符(*)来定义指针.在逗号分割的标识符列表中,每个将被用作指针的标识符前都必须加上解引用操作符(*),,在下面的例子中,lp是一个指向long类型对象的指针,而lp2则是一个long类型的数据对象,不是指针:
long*lp, lp2;
在下面的例子中,fp是一个float型的数据对象,而fp2是一个指向float型对象的指针;
floatfp, *fp2;
为了清楚,最好写成 string *ps,而不是string* ps;如果程序员后来想定义第二个字符串指针,可能会造成的错误,string* ps, ps2;
当指针持有0值时,表明它没有指向任何对象,或持有一个同类型的数据对象的地址,
已知ival的定义:
intival = 1024;
下面的定义以及对两个指针pi 和pi2的赋值都是合法的;
int*pi = 0;//pi被初始化为”没有指向任何对象”
int*pi2 = &ival;//pi2 被初始化为ival地址
pi= pi2;//pi和pi2都指向ival
pi2= 0;//pi2没有指向任何对象
指针不能持有非法地址值,例如,先买的赋值将导致编译错误:
pi= ival;//pi被赋以int值ival是错误的
指针不能被初始化或赋值为其它类型对象的地址值.例如,已知以下定义:
doubledval;
double*pd = &davl;
那么下面两条语句都会引起编译时错误:
pi= pd;
pi= &dval;
并不是说pi在物理上不能持有与dval相关联的内存的地址,它能够,但是不允许,因为虽然pi和pd能够持有同样的地址值,但对那块内存的存储布局和内容的解释却完全不同.
当然,我们要做的仅仅是持有地址值(可能是把一个地址同另一个地址做比较),那么指针的实际类型就不重要了.C++提供了一种特殊的指针类型来支持这种需求:空(void*)类型指针,它可以被任何数据指针类型的地址值赋值(函数指针不能赋值给它)
void*pv = pi;
pv= pd;
void*表明相关的值是个地址,但该地址的对象类型不知道.我们不能够操作空类型指针所指向的对象,只能传送该地址值或将它与其他地址值做比较,
已知一个int型指针对象pi 当我们写下pi时
// 计算包含在pi内部的地址值
// 类型int*
pi;
这将计算pi当前持有的地址值当我们写下&pi时
// 计算pi的实际地址
// 类型: int**
π
这将计算指针对象pi被存储的位置的地址那么怎样访问pi指向的对象呢,在缺省情况下我们没有办法访问pi指向的对象以对这个对象进行读或者写的操作
为了访问指针所指向的对象我们必须解除指针的引用C++提供了解引用操作符*dereference operator 来间接地读和写指针所指向的对象例如已知下列定义
int ival = 1024,ival2 = 2048;
int *pi =&ival;
下面给出了怎样解引用pi以便间接访问ival
// 解除pi的引用, 为它所指向的对象ival
// 赋以ival2的值
*pi = ival2;
// 对于右边的实例, 读取pi所指对象的值
// 对于左边的实例则把右边的表达式赋给对象
*pi = abs( *pi); // ival = abs(ival);
*pi = *pi +1; // ival = ival + 1;
我们知道当取一个int型对象的地址时
int *pi = &ival;
结果是int*——即指向int的指针当我们取指向int型的指针的地址时
int **ppi = π
结果是int**——即指向int指针的指针当我们解引用ppi时
int *pi2 = *ppi;
我们获得指针ppi持有的地址值——在本例中即pi持有的值而pi又是ival的地址,为了实际地访问到ival 我们需要两次解引用ppi 例如
cout << "The value of ival\n"
<< "direct value: " << ival <<"\n"
<< "indirect value: " << *pi <<"\n"
<< "doubly indirect value: " << **ppi
<< endl;
下面两条赋值语句的行为截然不同但它们都是合法的第一条语句增加了pi指向的数
据对象的值而第二条语句增加了pi包含的地址的值
int i, j, k;
int *pi =&i;
// i加2 (i = i + 2)
*pi = *pi + 2;
// 加到pi包含的地址上
pi = pi + 2;
指针可以让他的地址值增加或减少一个整数值,这类指针操作,被称为指针的算术运算.这种操初看上去并不直观,我们总认为是数据对象的加法,而不是离散的十进制数值的加法,.指针加2意味着给指针持有的地址值增加了该类型两个对象的长度,例如,假设一个char是一个字节,一个int是4个字节,一个double是8个字节,那么指针加2是给其持有的地址值增加2,8,还是16,完全取决于指针的类型是char,int还是double.
实际上,只有指针指向数组元素时,我们才能保证较好地运用指针的算术运算,在前面的例子中,我们不能保证3个整数变量连续存储在内存中,因此,lp+2可能,也可能不产生一个有效的地址,这取决于该位置上实际存储的是什么.指针算术运算的典型用法是遍历一个数组.例如::
intia[ 10 ];
int *iter =&ia[0];
int *iter_end =&ia[10];
while ( iter !=iter_end ) {
do_something_with_value( *iter );
++iter; // 现在iter指向下一个元素
}
来自C++ primer