1.在表达式中使用数组名时,该名字会自动转换为指向数组第一个元素的指针:
int ia[] = {0,2,4,6,8}; int *ip = ia; // ip points to ia[0]
如果希望使指针指向数组中的另一个元素,则可使用下标操作符给某个元素定位,然后用取地址操作符 & 获取该元素的存储地址:
ip = &ia[4]; // ip points to last element in ia
2.下标和指针
在使用下标访问数组时,实际上是对指向数组元素的指针做下标操作。只要指针指向数组元素,就可以对它进行下标操作:
int *p = &ia[2]; // ok: p points to the element indexed by 2 int j = p[1]; // ok: p[1] equivalent to *(p + 1), // p[1] is the same element as ia[3] int k = p[-2]; // ok: p[-2] is the same element as ia[0]
计算数组的超出末端指针
vector 类型提供的 end 操作将返回指向超出 vector 末端位置的一个迭代器。这个迭代器常用作哨兵,来控制处理vector 中元素的循环。类似地,可以计算数组的超出末端指针的值:
const size_t arr_size = 5; int arr[arr_size] = {1,2,3,4,5}; int *p = arr; // ok: p points to arr[0] int *p2 = p + arr_size; // ok: p2 points one past the end of arr // use caution -- do not dereference!
本例中,p 指向数组 arr 的第一个元素,在指针 p 上加数组长度即可计算出数组arr 的超出末端指针。p 加 5 即得p 所指向的元素后面的第五个 int 元素的地址——换句话说,p + 5指向数组的超出末端的位置。
C++ 允许计算数组或对象的超出末端的地址,但不允许对此地址进行解引用操作。而计算数组超出末端位置之后或数组首地址之前的地址都是不合法的。
3.用指针写迭代器风格的for循环
用指针编写以下程序:
const size_t arr_sz = 5; int int_arr[arr_sz] = { 0, 1, 2, 3, 4 }; // pbegin points to first element, pend points just after the last for (int *pbegin = int_arr, *pend = int_arr + arr_sz; pbegin != pend; ++pbegin) cout << *pbegin << ' '; // print the current element
C++ 允许使用指针遍历数组。和其他内置类型一样,数组也没有成员函数。因此,数组不提供 begin 和 end 操作,程序员只能自己给指针定位,使之分别标志数组的起始位置和超出末端位置。可在初始化中实现这两个指针的定位:初始化指针 pbegin 指向int_arr 数组的第一个元素,而指针pend 则指向该数组的超出末端的位置:
指针 pend 是标志 for 循环结束的哨兵。for 循环的每次迭代都会使pbegin 递增 1 以指向数组的下一个元素。第一次执行for 循环时,pbegin 指向数组中的第一个元素;第二次循环,指向第二个元素;这样依次类推。当处理完数组的最后一个元素后,pbegin 再加 1 则与pend 值相等,表示整个数组已遍历完毕。
4.指针和 const 限定符
1.指向 const 对象的指针
我们使用指针来修改其所指对象的值。但是如果指针指向 const 对象,则不允许用指针来改变其所指的 const 值。为了保证这个特性,C++ 语言强制要求指向 const 对象的指针也必须具有 const 特性:
const double *cptr; // cptr may point to a double that is const
这里的 cptr 是一个指向 double 类型 const 对象的指针,const 限定了cptr 指针所指向的对象类型,而并非cptr 本身。也就是说,cptr 本身并不是 const。在定义时不需要对它进行初始化,如果需要的话,允许给cptr 重新赋值,使其指向另一个const 对象。但不能通过 cptr 修改其所指对象的值:
*cptr = 42; // error: *cptr might be const
允许把非 const 对象的地址赋给指向 const 对象的指针,例如:
double dval = 3.14; // dval is a double; its value can be changed cptr = &dval; // ok: but can't change dval through cptr
dval的值不能通过cptr修改,但可以通过其他方式修改:
dval = 3.14159; // dval is not const *cptr = 3.14159; // error: cptr is a pointer to const double *ptr = &dval; // ok: ptr points at non-const double *ptr = 2.72; // ok: ptr is plain pointer cout << *cptr; // ok: prints 2.72如果把指向 const 的指针理解为“自以为指向 const 的指针”,这可能会对理解有所帮助。
2.const 指针
除指向 const 对象的指针外,C++ 语言还提供了const 指针——本身的值不能修改:
int errNumb = 0;
int *const curErr = &errNumb; // curErr is a constant pointer
我们可以从右向左把上述定义语句读作“curErr 是指向 int 型对象的 const 指针”。与其他 const 量一样,const 指针的值不能修改,这就意味着不能使 curErr 指向其他对象。任何企图给 const 指针赋值的行为(即使给 curErr 赋回同样的值)都会导致编译时的错误:
curErr = curErr; // error: curErr is const
与任何 const 量一样,const 指针也必须在定义时初始化。
3.指向 const 对象的 const 指针
还可以如下定义指向const 对象的const 指针:
const double pi = 3.14159;
// pi_ptr is const and points to a const object
const double *const pi_ptr = π
本例中,既不能修改 pi_ptr 所指向对象的值,也不允许修改该指针的指向(即 pi_ptr 中存放的地址值)。可从右向左阅读上述声明语句:“pi_ptr 首先是一个 const 指针,指向 double 类型的 const 对象”。