Дополнение к главе 1. Переменные и поиск ошибок


Объявление, обмен значений и таблицы трассировки

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

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

Важно различать объявление переменной (declaration) и присваивание ей значения (assignment).

Правильный пример:

1
2
int x;      // Объявление
x = 10;     // Присваивание

Частая ошибка:

1
2
int y;
y = y + 1; // Ошибка: у локальной переменной y ещё нет начального значения

Во второй программе возникнет ошибка Use of unassigned local variable 'y' — «Использование локальной переменной y, которой не присвоено значение». Чтобы вычислить y + 1, нужно сначала прочитать прежнее значение y, а программа его ещё не задала.

Исправьте пример, присвоив y начальное значение:

int y = 0;
y = y + 1;
Console.WriteLine(y); // 1

Visual Studio отмечает ошибки, которые мешают компиляции — переводу программы в исполняемый вид. Откройте список ошибок и прочитайте сообщение вместе с номером строки. Не ограничивайтесь красным подчёркиванием: текст сообщения подсказывает причину.

Сообщение Visual Studio об использовании переменной без присвоенного значения

Дополнительное видео на иврите: как читать ошибки Visual Studio

Видео показывает, как использовать сообщения Visual Studio для исправления ошибок компиляции. Просмотр необязателен; основной приём описан выше.

Обмен значений двух переменных

Представьте две чашки: в чашке A зелёный чай, а в чашке B — чёрный. Как поменять их содержимое, не смешивая чай?

Понадобится третья, пустая чашка temp:

  1. Перелейте содержимое A в temp.
  2. Перелейте содержимое B в освободившуюся A.
  3. Перелейте содержимое temp в освободившуюся B.

При обмене значений в программе такую же роль играет временная переменная. Она сохраняет значение, которое иначе было бы потеряно после присваивания.

Прочитайте два целых числа в переменные a и b, поменяйте значения местами и выведите результат. Сначала попробуйте самостоятельно.

Решение с временной переменной

Этот фрагмент заменяет метод Main внутри класса Program из предыдущей главы.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void Main()
{
    Console.WriteLine("Введите первое число:");
    int a = int.Parse(Console.ReadLine());

    Console.WriteLine("Введите второе число:");
    int b = int.Parse(Console.ReadLine());

    Console.WriteLine($"До обмена: a = {a}, b = {b}");

    int temp = a;  // Сохраняем исходное значение a
    a = b;         // Копируем значение b в a
    b = temp;      // Копируем исходное значение a в b

    Console.WriteLine($"После обмена: a = {a}, b = {b}");
}

Например, для a = 5 и b = 3 после обмена получится a = 3 и b = 5.

Запись $"До обмена: a = {a}, b = {b}" использует интерполяцию строк. Знак $ перед кавычками позволяет помещать выражения в фигурные скобки: вместо {a} в строку подставится значение a. Это ещё один способ объединить текст и значения без нескольких знаков +.

Сравните обе формы:

Console.WriteLine("a = " + a + ", b = " + b);
Console.WriteLine($"a = {a}, b = {b}");

В упражнениях 1.2 есть похожая задача. Посмотрите, как понятные имена переменных помогают прочитать алгоритм.

Почему следующий код не выполняет обмен?

a = b;
b = a;
Проверить объяснение

После первой строки в a уже находится значение b. Вторая строка копирует это же значение обратно в b. Обе переменные содержат исходное значение b, а исходное значение a потеряно.

Что такое таблица трассировки

Таблица трассировки показывает, как меняются значения переменных при выполнении команд. Она помогает предсказать результат, найти ошибку и понять назначение алгоритма.

Проследите за такой программой. Номера в левом столбце таблицы соответствуют номерам строк в примере:

1
2
3
4
5
6
7
8
9
10
11
static void Main(string[] args)
{
    int a = 5;       // Начальное значение a
    int b = 3;       // Начальное значение b

    a = a + b;
    b = a - b;
    a = a - b;

    Console.WriteLine($"a = {a}, b = {b}");
}
Строка a b
3 5 —
4 — 3
6 8 —
7 — 5
8 3 —

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

Итоговый вывод:

a = 3, b = 5

Как сформулировать назначение алгоритма

После трассировки ответьте: какую задачу решает программа? Перечисление команд («сначала складывает, потом вычитает…») ещё не объясняет общий результат.

Назначение программы

Программа меняет местами исходные значения a и b. В отличие от первого решения, здесь используются сложение и вычитание без временной переменной.

Обозначим исходные значения буквами A и B:

  1. После a = a + b; в a находится A + B.
  2. После b = a - b; в b находится (A + B) − B, то есть A.
  3. После a = a - b; в a находится (A + B) − A, то есть B.

Для обычных программ способ с temp понятнее и не требует сложения. При сложении больших чисел можно выйти за диапазон int; поведение тогда зависит от режима проверки переполнения. В этом примере используются небольшие значения.

Попробуйте выполнить трассировку для a = 9, b = 2, а затем для двух равных чисел. Предскажите вывод до запуска.

Дополнительно: поиск на сайте и оформление кода в документе

Для поиска материала используйте ключевые слова, например Console.ReadLine, char или название темы. Видео ниже — на иврите, просмотр необязателен.

Если вы храните решения в Google Docs, оставляйте отступы и используйте моноширинный шрифт, чтобы структура кода была видна. Дополнение Code Blocks может помочь с оформлением, но для выполнения программы оно не требуется: запускайте код в среде C#.

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

Содержание курса