const类型限定符可以把一个变量定义为常量从而避免了人为的不必要的对指定数据的修改。但同时const对变量限定后的一系列情况也常常是我们感到迷惑,为此我们来一点一点解析const的情况(高手可以直接跳过)
const int bufSize = 512 ,定义bufSize 是一个常量并将其初始化为512, 在程序中任何改变这个值的企图都将导致编译错误因此它被称为是只读的read- only 例如
// 错误企图写入const 对象
if ( bufsize = 0 ) ...
因为常量在定义后就不能被修改,所以它必须被初始化。未初始化的常量定义将导致编译错误:
const double pi; // 错误: 未初始化的常量
一旦一个常量被定义了,我们就不能改变与const 对象相关联的值,另一方面我们能把它的地址赋值给一个指针吗?例如下面代码是否可行
const double minWage = 9.60;
// ok? error?
double *ptr = &minWage;
这是否可行呢?minWage 是一个常量对象因此它不能被改写为一个新的值,但是ptr是一个普通指针,没有什么能阻止我们写出这样的代码:
*ptr += 1.40; // 修改了minwage!
一般编译器不能跟踪指针在程序中任意一点指向的对象,这种内部工作需要进行数据流分析, 通常由单独的优化器optimizer 组件来完成,允许非const对象的指针指向一个常量对象,把试图通过该指针间接地改变对象值的动作标记为非法的。这对编译器来说是不可行的,因而任何试图将一个非const 对象的指针指向一个常量对象的动作都将引起编译错误。
但这并不意味着我们不能间接地指向一个const 对象,也就是说我们必须声明一个指向常量的指针来做这件事例如
const double *cptr;
cptr 是一个指向double 类型的const 对象的指针,我们可以从右往左把这个定义读为“cptr 是一个指向double 类型的被定义成const 的对象的指针”。此中微妙在于cptr 本
身不是常量我们可以重新赋值cptr 使其指向不同的对象但不能修改cptr 指向的对象
例如
const double *pc = 0;
const double minWage = 9.60;
// ok: 不能通过pc 修改minWage
pc = &minWage;
double dval = 3.14;
// ok: 不能通过pc 修改dval
// 虽然dval 本身不是一个常量
pc = &dval; // ok
dval = 3.14159; // ok
*pc = 3.14159; // 错误
const 对象的地址只能赋值给指向const 对象的指针例如pc, 但是指向const 对象的指针可以被赋以一个非const 对象的地址例如
pc = &dval;
虽然dval 不是常量但试图通过pc 修改它的值仍会导致编译错误,因为在运行程序的任意一点上,编译器不能确定指针所指的实际对象,在实际的程序中指向const 的指针常被用作函数的形式参数它作为一个约定来保证被传递给函数的实际对象在函数中不会被修改
例如
// 在实际的程序中, 指向常量的指针
// 往往被用作函数参数
int strcmp( const char *str1, const char *str2 );
我们可以定义一个const 指针指向一个const 或一个非const 对象例如
int errNumb = 0;
int *const curErr = &errNumb;
curErr 是指向一个非const 对象的const 指针,我们可以从右拄左把定义读作“curErr是一个指向int 类型对象的const 指针“,这意味着不能赋给curErr 其他的地址值但可以
修改curErr 指向的值
下面的代码说明我们可以怎样使用curErr
if ( *curErr ) {
errorHandler();
*curErr = 0; // ok: 重置指针所指的对象
}
试图给const 指针赋值会在编译时刻被标记为错误
curErr = &myErrNumb; // 错误
指向const 对象的const 指针的定义就是将前面两种定义结合起来例如
const double pi = 3.14159;
const double *const pi_ptr = π
在这种情况下pi_ptr 指向的对象的值以及它的地址本身都不能被改变,我们可以从右往左将定义读作”pi_ptr 是指向被定义为const 的double 类型对象的const 指针“。