Объявление (declaration) сообщает компилятору, что такое имя существует. Определение (definition) действительно создаёт объект.

Зачем нужно

В C++ имя должно быть объявлено до использования. Этот код не компилируется, хотя x — настоящая глобальная переменная:

int main() {
  int y = 2;
  std::cout << x + 3 * y << '\n';  // ошибка
  return 0;
}
 
int x = 1;

В момент чтения строки с std::cout компилятор ещё не знает, что такое x.

Как работает

Имя можно заранее объявить, а переменную определить позже. Для этого есть ключевое слово extern:

int main() {
  int y = 2;
  extern int x;  // объявление без определения
  std::cout << x + 3 * y << '\n';
  return 0;
}
 
int x = 1;  // определение x

extern int x; объекта не создаёт — только обещает компилятору, что переменная x типа int где-то определена. Создаёт её строка int x = 1;.

Обычное int x; внутри функции — это объявление и определение сразу: объект уже существует, хотя начального значения у него нет. Об этом — Инициализация переменных.

Тонкости

  • В учебном коде проще писать глобальные переменные до функций, и extern не понадобится.
  • Различие объявления и определения дальше будет встречаться постоянно: в функциях, заголовочных файлах и раздельной компиляции — см. Подключение заголовков и Компиляция и сборка.
  • Объявление делает имя доступным, но только с места объявления и в пределах его области видимости.

Частые ошибки

  • Определить глобальную переменную ниже функции, которая её использует, и удивляться was not declared in this scope.
  • Считать, что объявление всегда создаёт объект: extern int x; ничего не создаёт.

Углубление — L05

Функции подчиняются тому же правилу: имя должно быть объявлено до вызова. Объявление функции — заголовок без тела, с точкой с запятой; определение — с телом:

int Min(int x, int y);  // объявление
 
int main() {
  return Min(10, 20);  // OK: Min уже объявлена
}
 
int Min(int x, int y) {  // определение
  return x < y ? x : y;
}

Без первой строки вызов — Min is not declared here. В объявлении функции имена параметров можно опустить: int Min(int, int);. Ключевое слово extern для функций не нужно — объявление без тела и так ничего не создаёт.

Правило одного определения (One Definition Rule, ODR). Согласованные объявления можно повторять сколько угодно; определение с телом — одно, даже если второе тело совпадает с первым (redefinition). Если функцию объявили и вызвали, но не определили нигде, компилятор пропустит программу, а упадёт компоновка. Подробнее — Функция.

Не из лекции

Про ненужность extern у функций лекция не говорит — это пояснение к разнице с примером из L02.