2.2.2. Объявления и определения переменных
Для обеспечения возможности разделить программу на несколько логических частей язык С++ предоставляет технологию, известную как раздельная компиляция (separate compilation). Раздельная компиляция позволяет составлять программу из нескольких файлов, каждый из которых может быть откомпилирован независимо.
При разделении программы на несколько файлов необходим способ совместного использования кода этих файлов. Например, код, определенный в одном файле, возможно, должен использовать переменную, определенную в другом файле. В качестве конкретного примера рассмотрим объекты std::cout и std::cin. Классы этих объектов определены где-то в стандартной библиотеке, но все же наши программы могут использовать их.
Внимание! Неинициализированные переменные — причина проблем во время выполнения
Значение неинициализированной переменной неопределенно. Попытка использования значения неинициализированной переменной является ошибкой, которую зачастую трудно обнаружить. Кроме того, компилятор не обязан обнаруживать такие ошибки, хотя большинство из них предупреждает, по крайней мере, о некоторых случаях использования неинициализированных переменных.
Что же произойдет при использовании неинициализированной переменной с неопределенным значением? Иногда (если повезет) программа отказывает сразу, при попытке доступа к объекту. Обнаружив место, где происходит отказ, как правило, довольно просто выяснить, что его причиной является неправильно инициализированная переменная. Но иногда программа срабатывает, хотя результат получается ошибочным. Возможен даже худший вариант, когда на одной машине результаты получаются правильными, а на другой происходит сбой. Кроме того, добавление кода во вполне работоспособную программу в неподходящем месте тоже может привести к внезапному возникновению проблем.
Для поддержки раздельной компиляции язык С++ различает объявления и определения. Объявление (declaration) делает имя известным программе. Файл, который должен использовать имя, определенное в другом месте, включает объявление для этого имени. Определение (definition) создает соответствующую сущность.
Объявление переменной определяет ее тип и имя. Определение переменной — это ее объявление. Кроме задания имени и типа, определение резервирует также место для ее хранения и может снабдить переменную исходным значением.
Чтобы получить объявление, не являющееся также определением, добавляется ключевое слово extern и можно не предоставлять явный инициализатор.
extern int i; // объявить, но не определить переменную i
int j; // объявить и определить переменную j
Любое объявление, которое включает явный инициализатор, является определением. Для переменной, определенной как extern (внешняя), можно предоставить инициализатор, но это отменит ее определение как extern. Объявление внешней переменной с инициализатором является ее определением:
extern double pi = 3.1416; // определение
Предоставление инициализатора внешней переменной в функции является ошибкой.
На настоящий момент различие между объявлением и определением может показаться неочевидным, но фактически оно очень важно. Использование переменной в нескольких файлах требует объявлений, отдельных от определения. Чтобы использовать ту же переменную в нескольких файлах, ее следует определить в одном, и только одном файле. В других файлах, где используется та же переменная, ее следует объявить, но не определять.
Более подробная информация о том, как язык С++ поддерживает раздельную компиляцию, приведена в разделах 2.6.3 и 6.1.3.
Упражнения раздела 2.2.2
Упражнение 2.11. Объясните, приведены ли ниже объявления или определения.
(a) extern int ix = 1024;
(b) int iy;
(c) extern int iz;
Ключевая концепция. Статическая типизация
Язык С++ обладает строгим статическим контролем типов (statically typed) данных. Это значит, что проверка соответствия значений заявленным для них типам данных осуществляется во время компиляции. Сам процесс проверки называют контролем соответствия типов (type-checking), или типизацией (typing).
Как уже упоминалось, тип ограничивает операции, которые можно выполнять с объектом. В языке С++ компилятор проверяет, поддерживает ли используемый тип операции, которые с ним выполняют. Если обнаруживается попытка сделать нечто, не поддерживаемое данным типом, компилятор выдает сообщение об ошибке и не создает исполняемый файл.
По мере усложнения рассматриваемых программ будет со всей очевидностью продемонстрировано, что строгий контроль соответствия типов способен помочь при поиске ошибок в исходном коде. Однако последствием статической проверки является то, что тип каждой используемой сущности должен быть известен компилятору. Следовательно, тип переменной необходимо объявить прежде, чем эту переменную можно будет использовать.