Menu

Переменные в C++: объявление, инициализация и область видимости

Как работают переменные в C++ - объявление с указанием типа, разница между присваиванием и инициализацией, инициализация фигурными скобками, правила именования и область видимости, которая определяет, где переменная живёт и умирает.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Что такое переменная

Переменная - это именованный участок памяти, который хранит значение. В C++ у каждой переменной есть фиксированный тип - выбранный при объявлении -, и этот тип никогда не меняется. Именно поэтому C++ является языком со статической типизацией: компилятор знает тип каждой переменной ещё до запуска программы и отказывается компилировать код, который хранит значение неправильного вида.

Объявление состоит из трёх частей: типа, имени и (почти всегда) начального значения.

Читайте int age = 30; как «создай int по имени age и положи в него 30». Точка с запятой завершает инструкцию, как и показано на странице о комментариях для каждой инструкции. Обратите внимание, что isActive вывелось как 1: bool по умолчанию отображается как 1/0 - на этом строится страница о типах данных.

Инициализация vs. присваивание

Эти две вещи выглядят похоже, но это разные операции, и эта разница - одна из важнейших идей на этой странице.

Инициализация даёт переменной её первое значение как часть объявления. Присваивание меняет значение уже существующей переменной.

Можно объявить без инициализации, а присвоить позже, но будьте осторожны:

Это работает, потому что мы присвоили score до того, как прочитали. Опасный вариант - прочитать его сначала, об этом дальше.

Ловушка неинициализированной переменной

Это классическая ловушка C++, которой нет во многих других языках. Чтение локальной переменной, которая была объявлена, но никогда не получала значения, - это неопределённое поведение: переменная содержит те байты, что случайно оказались в этой памяти.

int score;             // не инициализирована
std::cout << score;    // неопределённое поведение - печатает мусор, или «работает», или падает

Компилятор спокойно соберёт это. Может вывести 0, может вывести 32766, и может вести себя по-разному при каждом запуске или на каждой машине - из-за чего такие ошибки мучительно отлавливать. Две меры защиты:

  • Всегда инициализируйте при объявлении. int score = 0; ничего не стоит и полностью устраняет проблему.
  • Включите предупреждения. Компиляция с -Wall -Wextra заставляет компилятор отмечать множество неинициализированных чтений до того, как они вас укусят.

Для каждой локальной переменной предпочитайте первый вариант: дайте ей разумное начальное значение в тот же момент, когда создаёте.

Стили инициализации: =, () и {}

C++ предлагает несколько способов записать начальное значение. В основном они делают одно и то же, но у инициализации фигурными скобками есть одна дополнительная мера безопасности, о которой стоит знать.

Причина прибегать к {} в том, что она отвергает сужающие преобразования (narrowing) - присваивания, которые молча теряли бы данные. С = дробное значение тихо усекается; с {} компилятор вас останавливает:

int x = 3.9;    // компилируется - x молча становится 3 (.9 отбрасывается)
int y{3.9};     // ошибка компиляции - сужение из double в int не допускается

Поймать это на этапе компиляции - именно то, что вам нужно. Распространённая современная привычка - использовать {} по умолчанию ради этой дополнительной проверки и прибегать к = только тогда, когда инициализация копированием читается естественнее.

Правила и соглашения об именах

C++ навязывает несколько жёстких правил, а поверх них все добавляют соглашения. Правила: имя может содержать буквы, цифры и _; оно не может начинаться с цифры; оно не может быть зарезервированным ключевым словом (например, int или return); и оно чувствительно к регистру (age и Age - две разные переменные). Избегайте имён, которые начинаются с подчёркивания, за которым следует заглавная буква, или содержат два подчёркивания подряд - они зарезервированы для реализации.

Соглашения, которым следует большинство кода на C++:

  • Переменные используют snake_case или camelCase - выберите одно и придерживайтесь его: item_count или itemCount.
  • Имена должны описывать значение: count, а не c; user_email, а не x.

Понятные имена - это не украшение, это то, как ваше будущее «я» будет читать код. total_price = item_count * price_per_item объясняет себя сам так, как t = c * p не сможет никогда.

Область видимости переменных

Переменная существует только внутри блока - { ... } -, где она объявлена, и уничтожается на закрывающей фигурной скобке. Это её область видимости (scope). Переменная, объявленная внутри цикла или блока if, невидима снаружи:

И i, и square принадлежат циклу и исчезают, когда он завершается; total объявлена во внешнем блоке, поэтому она сохраняется. Внутренний блок также может перекрыть (shadow) имя из внешнего блока: новая переменная с тем же именем временно скрывает внешнюю, пока внутренний блок не закроется, - это частый источник путаницы, поэтому избегайте повторного использования имён во вложенных областях видимости.

Практический вывод: объявляйте каждую переменную в наименьшем блоке, которому она нужна, и инициализируйте её там же. Узкая область видимости означает меньше имён, борющихся за ваше внимание, и меньше шансов прочитать значение далеко от того места, где оно было задано.

Далее: Типы данных

Каждая переменная на этой странице начиналась с типа - int, double, string, bool. Следующая страница подробно разбирает типы данных C++: фундаментальные типы и их размеры, знаковые и беззнаковые целые, float vs. double, char и как выбрать правильный тип для задачи.

Часто задаваемые вопросы

Как объявить переменную в C++?

Напишите тип, затем имя, а затем при желании задайте значение: int age = 30;. Тип (int) зафиксирован на всю жизнь переменной; имя (age) - это то, как вы к ней обращаетесь. Можно объявить и без значения - int age; -, но для локальной переменной это оставляет в ней мусор до тех пор, пока вы не присвоите значение, поэтому всегда инициализируйте её прямо в месте объявления.

В чём разница между инициализацией и присваиванием в C++?

Инициализация даёт переменной её первое значение как часть объявления: int x = 5; или int x{5};. Присваивание меняет значение уже существующей переменной: x = 7;. Различие важно, потому что чтение локальной переменной, которая была объявлена, но никогда не инициализирована, - это неопределённое поведение.

Что произойдёт, если использовать неинициализированную переменную в C++?

Чтение значения неинициализированной локальной переменной - это неопределённое поведение (undefined behavior): переменная содержит те байты, что уже оказались в этой памяти, поэтому программа может вывести случайное число, сработать по счастливой случайности или упасть. Компилятор вас не остановит (хотя многие предупреждают при -Wall), поэтому решение - всегда давать локальным переменным значение при объявлении.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ