Существует и третье место, куда можно поместить const:
float const * pfc; // то же, что и const float * pfc;
Как отражает коммен тарий, помещение const после имени типа и перед символом * означает что указатель не может использоваться для изменения значения, па которое он ссылается. Выражаясь кратко, ключевое слово const, находящееся где угодно слева от символа *, делает константными данные, а справа — делает константным сам указатель.
Распространенным использованием этого нового ключевого слова является объявление указателей, которые служат формальными параметрами функций. Например, пусть имеется функция по имени display() , которая отображает содержимое массива. Чтобы применить данную функцию, необходимо передать ей в качестве фактического аргумента имя массива, но имя массива одновременно представляет собой и его адрес. Это позволит функции изменять данные из вызывающей функции. Однако следующий прототип предотвращает такое изменение:
void display(const int array[], int limit);
В прототипе и в заголовке функции объявление параметра const int array[] аналогично объявлению const int * array, и первое объявление говорит о том, что данные, на которые указывает array, не могут быть изменены.
Этой практики придерживается библиотека ANSI С. Если указатель используется только для предоставления функции доступа к значениям, он объявляется как указатель на квалифицированный посредством const тип. Если указатель применяется для изменения данных в вызывающей функции, то ключевое слово const не используется. Например, объявление функции strcat(), принятое в ANSI С, имеет такой вид:
char *strcat(char * restrict si, const char * restrict s2);
Вспомните, что функция strcat() добавляет копию второй строки в конец первой строки. Это приводит к модификации первой строки, но оставляет вторую строку неизмененной. Приведенное объявление отражает сказанное. Вскоре мы возвратимся к роли restrict.
Использование const с глобальными данными
Вспомните, что применение глобальных переменных считается рискованным подходом, поскольку данные открыты для ошибочного изменения любой частью программы. Такой риск исчезает, если данные являются константными, так что вполне разумно снабжать глобальные переменных квалификатором const. Можно иметь пе ременные const, массивы const и структуры const. (Структуры — это составной тип данных, который обсуждается в следующей главе.)
Однако необходимо соблюдать осторожность при совместном использовании константных данных в разных файлах. Для этого предусмотрены две стратегии. Первая заключается в следовании обычным правилам, применяемым в отношении внешних переменных — использование определяющих объявлений в одном файле и ссылочных объявлений (с ключевым словом extern) в других файлах:
Классы хранения, связывание и управление памятью 519
/* file1.с -- определение нескольких глобальных констант */ const double PI = 3.14159; const char * MONTHS [12] =
{"Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль",
"Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"];
/* file2.c -- использование констант, определенных где-то в другом месте */ extern const double PI; extern const * MONTHS]];
Второй подход предполагает помещение констант во включаемый файл. Здесь придется предпринять дополнительное действие, связанное с применением статического внешнего класса хранения:
/* constant.h -- определение нескольких глобальных констант */ static const double PI = 3.14159; static const char * MONTHS [12] =
{"Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль",
"Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь");
/* file1.с -- использование констант, определенных где-то в другом месте */ #include "constant.h"
/* file2.c -- использование констант, определенных где-то в другом месте */ #include "constant.h"
Если вы не укажете ключевое слово static, то включение заголовочного файла constant.h в file1.с и file2.c приведет к тому, что каждый файл будет иметь определяющее объявление того же самого идентификатора, что стандартом ANSI не поддерживается. (Тем не менее, некоторые компиляторы разрешают это.) Делая идентификатор внешним и статическим, вы фактически предоставляете каждому файлу о-г дельную копию данных. Такой прием не будет работать, если по замыслу файлы должны использовать эти данные для связи друг с другом, потому что каждый файл будет видеть только свою копию данных. Однако поскольку данные являются константными (из-за наличия ключевого слова const) и идентичными (т.к. оба файла включают тот же самый заголовочный файл), проблемы не возникают.
Преимущество подхода с заголовочным файлом состоит в том, что вы не обязаны помнить о применении определяющих объявлений в одном файле и ссылочных объявлений в другом; все файлы просто включают тот же самый заголовочный файл. Недостаток связан с тем, что данные дублируются. В предшествующих примерах это не приводит к значительной проблеме, но она может возникнуть, если в состав константных данных входят крупные массивы.
Читать дальше