Menu

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

Как объявить переменную в C, чем объявление отличается от инициализации, правила именования, объявление нескольких переменных сразу и почему неинициализированная переменная — самый частый баг новичка.

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

Переменная — это именованный участок памяти, который хранит значение, доступное вашей программе для чтения и изменения. В C вы сразу заявляете, какого рода значение она хранит, и этот выбор остаётся неизменным на всё время жизни переменной.

Объявление переменной

Форма такова: тип, имя, точка с запятой:

int age;
double price;
char grade;

Каждая строка резервирует достаточно памяти для соответствующего типа данных и привязывает к ней имя. Пока ничего не сохранено — место есть, но его содержимое — это то, что там оставило всё, что выполнялось раньше.

Значение можно задать в тот же момент, и это называется инициализацией:

Обратите внимание на спецификаторы формата: %d для int, %f для double, %c для char. printf не изучает переданные ему значения — он доверяет вашей строке формата, поэтому несоответствие выведет бессмыслицу, а не ошибку.

Присваивание после объявления

Когда переменная существует, вы меняете её с помощью =:

= в C — это присваивание, а не равенство. score = score + 10 читается как «взять то, что лежит в score, прибавить 10, положить результат обратно в score». Оператор сравнения — ==, и их путаница настолько частый баг, что ей посвящён отдельный раздел на странице логического типа.

Сокращение score += 10 делает то же самое меньшим числом символов; составные операторы присваивания покрывают всю арифметику.

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

Это самый частый баг новичков в C, и он не порождает ошибки:

Запустите. Вы можете увидеть 15 — правильный ответ. Можете увидеть огромное случайное число. Можете увидеть что-то другое при втором запуске или на другой машине. Всё это «корректное» поведение, потому что чтение неинициализированной локальной переменной — это неопределённое поведение: стандарт C не предъявляет никаких требований к тому, что произойдёт.

В отличие от многих языков, C не обнуляет ваши локальные переменные. Локальная переменная — это кусочек стека, а стек полон остатков от предыдущих вызовов функций. Иногда эти остатки случайно оказываются нулями, и именно это делает баг таким опасным: он часто работает при тестировании и ломается в продакшене.

Исправление — один символ:

Сделайте это привычкой: инициализируйте каждую переменную в точке объявления. Если разумного значения у вас пока действительно нет, 0 или NULL лучше мусора. И компилируйте с -Wall, который ловит большинство случаев:

warning: 'total' is used uninitialized in this function

Исключение — глобальные и static-переменные: они действительно обнуляются автоматически. Полагаться на это нормально, но явная запись = 0 документирует ваше намерение.

Объявление нескольких сразу

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

Это законно и часто используется для тесно связанных значений вроде x, y, z. Здесь же живёт классическое недоразумение:

int a = 0, b = 0, c = 0;   /* все три равны 0 */
int d, e, f = 0;           /* только f равно 0 - d и e содержат мусор */

Инициализатор привязывается к одному имени, а не ко всей строке. Когда значения различаются по смыслу, давайте каждому свою строку — так читается лучше и diff чище.

Объявление указателей в одну строку содержит ту же ловушку в более острой форме:

int *p, q;   /* p - указатель на int; q - обычный int */

* привязывается к имени, а не к типу. Объявляйте указатели по одному на строку.

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

Правила компилятора:

  • Только буквы, цифры и подчёркивания.
  • Нельзя начинать с цифры: total2 можно, 2total нельзя.
  • Нельзя использовать ключевое слово C: никаких переменных с именами int, return, for, double.
  • Регистр важен: count, Count и COUNT — три разные переменные.
  • Имена, начинающиеся с подчёркивания или содержащие двойное подчёркивание, зарезервированы для реализации. Не создавайте их.
int itemCount;       /* законно */
int item_count;      /* законно - обычный стиль C */
int _count;          /* законно, но зарезервировано - избегайте */
int item-count;      /* ошибка: '-' - оператор вычитания */
int 3rd_place;       /* ошибка: начинается с цифры */
int float;           /* ошибка: ключевое слово */

Соглашения, которым следует большая часть кода на C: snake_case для переменных и функций, UPPER_CASE для макросов-констант и имена, говорящие о том, что значение означает, а не какого оно типа. elapsed_ms лучше, чем t; customer_count лучше, чем n2.

Короткие имена по-прежнему уместны там, где область видимости крошечная: i как счётчик цикла понятен всем, и никому не нужно for (int loop_iteration_index = 0; ...).

Где живёт переменная

Место объявления переменной определяет, кто её видит и сколько она живёт. Это область видимости, и коротко всё выглядит так:

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

Объявления где угодно (C99 и позже)

В исходном C89 все объявления должны были идти до любой инструкции в блоке:

int main(void) {
    int i, sum = 0;      /* сначала все объявления */
    printf("Starting\n");
    for (i = 0; i < 10; i++) { sum += i; }
    return 0;
}

Начиная с C99 можно объявлять там, где значение впервые нужно, включая заголовок for:

Это лучший стиль: объявление стоит рядом с первым использованием, и i не может просочиться в остальную часть функции. Компилируйте с -std=c17 (или любым режимом C99+), и это работает везде.

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

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

Напишите тип, затем имя, затем точку с запятой: int age;. Можно сразу задать значение — int age = 30;, — это называется инициализацией и почти всегда именно то, что вам нужно.

В чём разница между объявлением и инициализацией переменной?

Объявление резервирует место и даёт ему имя (int count;). Инициализация кладёт в него значение в момент создания (int count = 0;). Объявленная, но неинициализированная локальная переменная содержит те байты, которые уже лежали по этому адресу, — её чтение является неопределённым поведением.

Какие правила действуют для имён переменных в C?

Только буквы, цифры и подчёркивания; имя не может начинаться с цифры; не может быть ключевым словом вроде int или for; регистр важен, поэтому total и Total — разные переменные. Имена, начинающиеся с подчёркивания, зарезервированы — избегайте их.

Можно ли объявлять переменные в любом месте функции C?

Начиная с C99 — да, объявляйте каждую переменную там, где она впервые нужна. В исходном стандарте C89 все объявления должны были стоять в начале блока, поэтому в старом коде перед любой полезной работой идёт стена объявлений.

Coddy programming languages illustration

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

НАЧАТЬ