4.2.6 常量指针和指向常量的指针
在上一个示例中,数组pstr无疑不需要在程序运行过程中修改,那些被指向的字符串也不需要修改,变量count也一样。确保程序不会错误地修改这些数据无疑是个好主意。保护变量count不被意外修改非常容易,我们可以这样写:
- const int count = (sizeof pstr)/(sizeof pstr[0]);
不过,指针数组应当更仔细地分析。我们是这样声明指针数组的:
- char* pstr[] = { "Robert Redford", // Initializing a pointer array
- "Hopalong Cassidy",
- "Lassie",
- "Slim Pickens",
- "Boris Karloff",
- "Oliver Hardy"
- };
数组中各个指针被初始化成"Robert Redford"、"Hopalong Cassidy"等字符串字面值的地址。字符串字面值的类型是const char数组,因此我们是在将const数组的地址存入非const的指针中。编译器之所以允许我们使用字符串字面值初始化char*数组的元素,只是为了向后兼容现有的代码。
如果试图用下面这条语句
- *pstr[0] = "Stan Laurel";
修改字符数组,则该程序将不能编译。
如果打算使用下面这条语句
- *pstr[0] = 'X';
使指针数组的一个元素指向某个字符,则该程序可以被编译,但将在执行这条语句时崩溃。
没有人希望运行时出现像程序崩溃这样的意外,我们当然有办法避免。更好的声明方法如下所示:
- const char* pstr[] = { "Robert Redford", // Array of pointers
- "Hopalong Cassidy", // to constants
- "Lassie",
- "Slim Pickens",
- "Boris Karloff",
- "Oliver Hardy"
- };
上面的代码明确指出,指针数组的元素指向const字符串。如果我们现在试图修改这些字符串,则将被编译器在编译时标记为错误。
当然,我们仍然可以合法地编写下面的语句:
- pstr[0] = pstr[1];
那些应该得到Redford先生的幸运者现在将得到Cassidy先生,因为上面两个指针现在都指向同一个姓名。注意,这条语句并没有改变指针数组元素指向的对象的值,改变的只是pstr[0]中存储的指针的值。我们应该禁止这种修改,因为有些人可能认为当年的Hoppy不像Robert那样成熟。为此,我们可以使用下面的语句:
- // Array of constant pointers to constants
- const char* const pstr[] = {"Robert Redford",
- "Hopalong Cassidy",
- "Lassie",
- "Slim Pickens",
- "Boris Karloff",
- "Oliver Hardy"
- };
总之,我们应该区分下面这三种与const、指针及指针指向的对象有关的情形:
指向常量对象的指针
指向某个对象的常量指针
指向常量对象的常量指针
第一种情况,我们不能修改被指向的对象,但可以使指针指向其他对象:
- const char* pstring = "Some text";
第二种情况,我们不能修改指针中存储的地址,但可以修改指针指向的对象:
- char* const pstring = "Some text";
在最后一种情况中,指针和被指向的对象都被定义成常量,因此都不能被修改:
- const char* const pstring = "Some text";
注意:
当然,这三种情形适用于指向任何类型的指针。这里使用指向char类型的指针只是为了说明问题。