*pc = 3.14159; // ошибка
Адрес константного объекта присваивается только указателю на константу. Вместе с тем, такому указателю может быть присвоен и адрес обычной переменной:
pc = dval;
Константный указатель не позволяет изменять адресуемый им объект с помощью косвенной адресации. Хотя dval в примере выше и не является константой, компилятор не допустит изменения переменной dval через pc. (Опять-таки потому, что он не в состоянии определить, адрес какого объекта может содержать указатель в произвольный момент выполнения программы.)
В реальных программах указатели на константы чаще всего употребляются как формальные параметры функций. Их использование дает гарантию, что объект, переданный в функцию в качестве фактического аргумента, не будет изменен этой функцией. Например:
// В реальных программах указатели на константы чаще всего
// употребляются как формальные параметры функций
int strcmp( const char *str1, const char *str2 );
(Мы еще поговорим об указателях на константы в главе 7, когда речь пойдет о функциях.)
Существуют и константные указатели. (Обратите внимание на разницу между константным указателем и указателем на константу!). Константный указатель может адресовать как константу, так и переменную. Например:
int errNumb = 0;
int *const currErr = errNumb;
Здесь curErr – константный указатель на неконстантный объект. Это значит, что мы не можем присвоить ему адрес другого объекта, хотя сам объект допускает модификацию. Вот как мог бы быть использован указатель curErr:
do_something();
if ( *curErr ) {
errorHandler();
*curErr = 0; // правильно: обнулим значение errNumb
}
Попытка присвоить значение константному указателю вызовет ошибку компиляции:
curErr = myErNumb; // ошибка
Константный указатель на константу является объединением двух рассмотренных случаев.
const double pi = 3.14159;
const double *const pi_ptr = pi;
Ни значение объекта, на который указывает pi_ptr, ни значение самого указателя не может быть изменено в программе.
Объясните значение следующих пяти определений. Есть ли среди них ошибочные?
(a) int i; (d) int *const cpi;
(b) const int ic; (e) const int *const cpic;
(c) const int *pic;
Какие из приведенных определений правильны? Почему?
(a) int i = -1;
(b) const int ic = i;
(c) const int *pic = ic;
(d) int *const cpi = ic;
(e) const int *const cpic = ic;
Используя определения из предыдущего упражнения, укажите правильные операторы присваивания. Объясните.
(a) i = ic; (d) pic = cpic;
(b) pic = ic; (i) cpic = ic;
(c) cpi = pic; (f) ic = *cpic;
3.6. Ссылочный тип
Ссылочный тип, иногда называемый псевдонимом, служит для задания объекту дополнительного имени. Ссылка позволяет косвенно манипулировать объектом, точно так же, как это делается с помощью указателя. Однако эта косвенная манипуляция не требует специального синтаксиса, необходимого для указателей. Обычно ссылки употребляются как формальные параметры функций. В этом разделе мы рассмотрим самостоятельное использование объектов ссылочного типа.
Ссылочный тип обозначается указанием оператора взятия адреса () перед именем переменной. Ссылка должна быть инициализирована. Например:
int ival = 1024;
// правильно: refVal - ссылка на ival
int refVal = ival;
// ошибка: ссылка должна быть инициализирована
int refVal2;
Хотя, как мы говорили, ссылка очень похожа на указатель, она должна быть инициализирована не адресом объекта, а его значением. Таким объектом может быть и указатель:
int ival = 1024;
// ошибка: refVal имеет тип int, а не int*
int refVal = ival;
int *pi = ival;
// правильно: ptrVal - ссылка на указатель
int *ptrVal2 = pi;
Определив ссылку, вы уже не сможете изменить ее так, чтобы работать с другим объектом (именно поэтому ссылка должна быть инициализирована в месте своего определения). В следующем примере оператор присваивания не меняет значения refVal, новое значение присваивается переменной ival – ту, которую адресует refVal.
int min_val = 0;