Урок 2.5 — Виды ошибок
Ошибки — это нормально. Научимся читать, понимать и исправлять их. Главный навык программиста.
Ошибки — это нормально. Даже опытные программисты с 20-летним стажем пишут код с ошибками. Разница между новичком и профессионалом не в том, что профессионал не делает ошибок, а в том, что он умеет их находить и исправлять. В этом уроке мы разберём три вида ошибок, с которыми ты столкнёшься, и научимся читать сообщения об ошибках — это навык, который сэкономит тебе сотни часов.
Три вида ошибок в C#
Все ошибки в программировании делятся на три категории:
Программа не скомпилируется. Код даже не запустится. Компилятор подсказывает, в чём проблема. Номер ошибки начинается с CS (C# Sharp). Пример: пропущенная точка с запятой, неверное имя переменной, лишняя скобка.
error CS1002: ; expected
Код успешно скомпилировался и запустился, но в какой-то момент произошло исключение (exception). Пример: деление на ноль, обращение к несуществующему элементу массива, попытка открыть несуществующий файл. Программа «падает» с красным текстом ошибки.
System.DivideByZeroException: Attempted to divide by zero.
Самые коварные ошибки. Код скомпилировался, запустился, не упал — но выдаёт неправильный результат. Компьютер не знает, что ты хотел сделать. Он просто выполняет инструкции. Если ты написал a + b вместо a * b — программа честно сложит, а не умножит.
Программа работает, но выдаёт 8 вместо 15.
⚠️ Важно: Логические ошибки сложнее всего найти. Компилятор их не видит. Программа не падает. Только ты сам можешь заметить, что результат не совпадает с ожидаемым. Поэтому так важны тесты (мы будем писать их в модуле 12).
Ошибки компиляции (CS????)
Ошибки компиляции возникают, когда ты нарушаешь синтаксис языка. C# — строго типизированный язык с чётким синтаксисом. Компилятор проверяет код до запуска и сообщает о проблемах. Каждая ошибка имеет код вида CSXXXX.
Рассмотрим самые частые ошибки компиляции у новичков:
1. Пропущенная точка с запятой ;
В C# каждая инструкция должна заканчиваться ;
int x = 5 // ❌ забыли точку с запятой
Console.WriteLine(x);
CS1002: ; expected
2. Использование необъявленной переменной
Переменную нужно объявить до использования.
Console.WriteLine(z); // ❌ z не объявлена
CS0103: The name 'z' does not exist in the current context
3. Несоответствие типов
C# не позволяет положить строку в int.
int number = "привет"; // ❌ string нельзя в int
CS0029: Cannot implicitly convert type 'string' to 'int'
4. Забыли фигурную скобку }
Фигурные скобки должны быть парными.
class Program
{
static void Main()
{
Console.WriteLine("Привет");
// ❌ забыли закрывающую скобку }
CS1513: } expected
5. Ключевое слово как имя переменной
Нельзя называть переменную int, class, if и т.д.
int int = 10; // ❌ int — ключевое слово
CS1519: Invalid token '=' in class, struct, or interface member declaration
6. Пропущенный using System;
Console.WriteLine живёт в пространстве имён System.
class Program {
static void Main() {
Console.WriteLine("Привет"); // ❌ нужен using System;
}
}
CS0103: The name 'Console' does not exist in the current context
💡 Как читать ошибку компиляции: В Visual Studio или Rider ошибка подчёркивается красной волнистой линией. В терминале (dotnet build) ты увидишь: Program.cs(5,13): error CS1002: ; expected. Это значит: файл Program.cs, строка 5, позиция 13. CS1002 — код ошибки. «; expected» — описание. Всегда начинай исправление с первой ошибки — часто она тянет за собой десятки других.
Ошибки времени выполнения (исключения)
Программа скомпилировалась, запустилась, но во время работы что-то пошло не так — возникает исключение (exception). Программа «падает» и выводит красный текст с информацией об ошибке. Это называется stack trace (трассировка стека).
Самые частые исключения у новичков:
int a = 10;
int b = 0;
int result = a / b; // ❌ DivideByZeroException!
System.DivideByZeroException: 'Attempted to divide by zero.'
💡 В C# деление целого числа на ноль вызывает исключение. Но деление double на ноль даёт Infinity, -Infinity или NaN.
string text = null;
int length = text.Length; // ❌ NullReferenceException!
System.NullReferenceException: 'Object reference not set to an instance of an object.'
💡 Это самое частое исключение в C#. Всегда проверяй, не равен ли объект null, перед тем как вызвать его метод.
string input = "abc";
int number = int.Parse(input); // ❌ FormatException!
System.FormatException: 'Input string was not in a correct format.'
💡 Поэтому мы используем TryParse — он не выбрасывает исключение, а возвращает false.
int[] numbers = { 10, 20, 30 };
Console.WriteLine(numbers[5]); // ❌ IndexOutOfRangeException!
System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'
💡 Массивы нумеруются с 0. Если в массиве 3 элемента, индексы 0, 1, 2. Индекс 5 — вне диапазона.
string text = File.ReadAllText("nofile.txt"); // ❌ FileNotFoundException
System.IO.FileNotFoundException: 'Could not find file...'
💡 Как читать stack trace: Красный текст ошибки содержит цепочку вызовов — от места, где исключение возникло, до места, где оно было поймано (или до Main, если не поймано). Читай снизу вверх: самая нижняя строчка — это место, где произошла ошибка. Строка содержит имя файла, номер строки и позицию.
Логические ошибки
Логические ошибки — самые опасные. Программа делает ровно то, что ты написал, но ты написал не то, что хотел. Ошибки компиляции и исключения хотя бы заметны — программа не компилируется или падает. Логическая ошибка может жить в коде годами, тихо выдавая неправильные результаты.
Пример 1: Неправильная формула
Нужно вычислить среднее арифметическое двух чисел:
int a = 5;
int b = 7;
int average = a + b / 2; // ❌ 5 + (7/2) = 5 + 3 = 8
int averageCorrect = (a + b) / 2; // ✅ (5+7)/2 = 12/2 = 6
Console.WriteLine(average); // 8 (неправильно!)
Console.WriteLine(averageCorrect); // 6
💡 Из-за приоритета операторов деление выполнилось раньше сложения. Скобки решают.
Пример 2: Путаница с = и ==
= — присваивание, == — сравнение. Перепутать легко.
int x = 5;
if (x = 10) // ❌ присваивание! x становится 10, условие всегда true
Console.WriteLine("x равен 10");
if (x == 10) // ✅ сравнение
Console.WriteLine("x равен 10");
💡 В C# компилятор предупредит о if (x = 10) — выдаст предупреждение CS1718. Но это не ошибка, код скомпилируется.
Пример 3: Границы циклов и массивов
«Ошибка на единицу» (off-by-one error) — классика.
int[] numbers = { 10, 20, 30, 40 };
// Хотели вывести все элементы:
for (int i = 0; i <= numbers.Length; i++) // ❌ <= должно быть <
Console.WriteLine(numbers[i]); // IndexOutOfRangeException на i=4
// Правильно:
for (int i = 0; i < numbers.Length; i++)
Console.WriteLine(numbers[i]);
Как искать логические ошибки?
- Вывод промежуточных результатов. Вставь
Console.WriteLine()в ключевых местах и смотри, что получается. - Debug (точка останова). В Visual Studio / Rider поставь точку останова (красная точка слева от строки) и смотри значения переменных в отладчике.
- Тесты. Напиши маленькие тесты для каждой функции. Если функция должна возвращать 6 при входных данных 2 и 3 — проверь это.
- Объясни код вслух. Буквально: сядь и расскажи коду (или игрушечной утке), что он делает. Очень часто ошибка находится в процессе объяснения.
Как читать сообщения об ошибках
Сообщения компилятора и исключения пугают новичков. Но на самом деле они — твой лучший друг. Они содержат всю информацию для исправления ошибки. Научись их читать.
Разбор ошибки компиляции
| Program.cs | Файл, в котором ошибка |
| (7,5) | Строка 7, позиция 5 |
| error CS1002 | Код ошибки (CS = C# Sharp) |
| ; expected | Описание: ожидалась точка с запятой |
Разбор stack trace исключения
at Program.Main() in /Users/user/Projects/App/Program.cs:line 8
| System.DivideByZeroException | Тип исключения |
| Attempted to divide by zero | Описание: попытка деления на ноль |
| at Program.Main() ... line 8 | Где произошла ошибка (метод, файл, строка) |
💡 Золотое правило: Прочитай всю ошибку от начала до конца. Не паникуй. В 90% случаев ошибка говорит прямо, что не так. «; expected» — очевидно, нужна точка с запятой. «Type or namespace definition, or end-of-file expected» — скорее всего, забыл закрывающую скобку }. Научись видеть в ошибке подсказку, а не приговор.
Как гуглить ошибки
Умение искать решение в интернете — один из самых важных навыков программиста. Все ошибки, которые ты встретишь, уже кто-то встречал до тебя. Вот стратегия поиска:
1. Скопируй точный текст ошибки
Не пересказывай своими словами. Скопируй весь текст ошибки. В идеале — код ошибки (CS1002) или имя исключения (DivideByZeroException).
2. Используй правильные ключевые слова
Гугли: C# CS1002, C# NullReferenceException, C# int.Parse FormatException. Добавь dotnet или .NET для актуальных результатов.
3. Читай StackOverflow
StackOverflow — главный сайт для программистов. Чаще всего первый результат поиска — вопрос с SO. Читай ответы с зелёной галочкой (принятый ответ).
4. Читай официальную документацию Microsoft
Документация Microsoft (learn.microsoft.com) — лучший источник. Статьи на русском: learn.microsoft.com/ru-ru/dotnet/. Если знаешь английский — используй английскую версию, она всегда новее.
5. Используй ChatGPT/Copilot
Современные AI-помощники отлично справляются с поиском ошибок. Скопируй код и текст ошибки — AI объяснит причину и предложит исправление. Но не копируй слепо — сначала пойми, что было не так.
⚠️ Лайфхак: как задавать вопросы про код
Когда просишь помощи (у коллеги, на StackOverflow, у AI), всегда показывай:
- Сам код (не скриншот, а текст)
- Текст ошибки (полностью)
- Что ты ожидал (входные данные → ожидаемый результат)
- Что получил (входные данные → реальный результат)
- Что уже попробовал (покажи, что ты не ленивый)
Большой пример — ошибки в реальной программе
Давай разберём программу, в которой намеренно допущены ошибки всех трёх типов. Попробуй найти их до того, как прочитаешь комментарии.
using System;
class Program
{
static void Main()
{
// ОШИБКА КОМПИЛЯЦИИ #1: пропущена точка с запятой
Console.WriteLine("Добро пожаловать в калькулятор!")
// ↑ ошибка CS1002
Console.Write("Введите первое число: ");
string input1 = Console.ReadLine();
Console.Write("Введите второе число: ");
string input2 = Console.ReadLine();
// ОШИБКА КОМПИЛЯЦИИ #2: необъявленная переменная
Console.WriteLine($"Вы ввели {num1} и {num2}");
// ↑ ошибка CS0103
// ОШИБКА ВРЕМЕНИ ВЫПОЛНЕНИЯ: если ввести не число
int a = int.Parse(input1);
int b = int.Parse(input2);
// Если input1 = "abc" → FormatException!
// ЛОГИЧЕСКАЯ ОШИБКА: неправильная формула
int sum = a + b;
int average = a + b / 2;
// ↑ деление выполнится раньше сложения!
// Нужно: (a + b) / 2
// ОШИБКА ВРЕМЕНИ ВЫПОЛНЕНИЯ: деление на ноль
int ratio = a / b;
// Если b = 0 → DivideByZeroException!
Console.WriteLine($"Сумма: {sum}");
Console.WriteLine($"Среднее: {average}");
Console.WriteLine($"Отношение: {ratio}");
}
}
Теперь та же программа, но исправленная:
using System;
class Program
{
static void Main()
{
Console.WriteLine("Добро пожаловать в калькулятор!");
Console.Write("Введите первое число: ");
string input1 = Console.ReadLine();
Console.Write("Введите второе число: ");
string input2 = Console.ReadLine();
// Безопасное преобразование с TryParse
if (int.TryParse(input1, out int a) && int.TryParse(input2, out int b))
{
int sum = a + b;
int average = (a + b) / 2; // скобки исправляют логическую ошибку
Console.WriteLine($"Вы ввели {a} и {b}");
Console.WriteLine($"Сумма: {sum}");
Console.WriteLine($"Среднее: {average}");
// Проверка деления на ноль
if (b != 0)
{
int ratio = a / b;
Console.WriteLine($"Отношение: {ratio}");
}
else
{
Console.WriteLine("Отношение: нельзя делить на ноль");
}
}
else
{
Console.WriteLine("Ошибка: нужно ввести числа, а не текст!");
}
}
}
TryParse (убирает FormatException), проверили деление на ноль (убирает DivideByZeroException), поставили скобки в формуле (убирает логическую ошибку) и не забыли точки с запятой. Все три типа ошибок обработаны.Практическое задание
Вот программа с 5 ошибками (всех трёх типов). Найди и исправь их:
using System
class Program
{
static void Main()
{
Console.Write("Сколько вам лет? ");
string input = Console.ReadLine()
int age = int.Parse(input);
if (input.Length > 2)
Console.WriteLine("Вы выглядите молодо для своего возраста!")
int months = age * 12;
int decades = age / 10;
int futureYear = 2050 - age;
Console.WriteLine($"Вам {age} лет");
Console.WriteLine($"Это {months} месяцев");
Console.WriteLine($"Это {decades} десятилетий");
Console.WriteLine($"В 2050 году вам будет {futureYear}");
}
}
👀 Показать ответ
using System; // ✔️ добавлена точка с запятой после using System
class Program
{
static void Main()
{
Console.Write("Сколько вам лет? ");
string input = Console.ReadLine(); // ✔️ точка с запятой
// ✔️ Добавлена проверка TryParse (была логическая ошибка:
// если ввести "abc" — FormatException)
if (!int.TryParse(input, out int age))
{
Console.WriteLine("Ошибка: введите число!");
return;
}
// ✔️ Исправлено условие: input.Length > 2 работает, но лучше
// проверять сам возраст. Если age > 100 — тогда "выглядите молодо"
if (age > 100)
Console.WriteLine("Вы выглядите молодо для своего возраста!");
int months = age * 12;
int decades = age / 10;
int futureYear = 2050 - age; // ✔️ ЛОГИЧЕСКАЯ ОШИБКА!
// Было: 2050 - age. Если age=20, то futureYear=2030.
// А должно быть: 2026 + (100 - age) — сколько лет до 100 + текущий год
// Или просто: сколько лет будет в 2050 = 2050 - 2026 + age.
Console.WriteLine($"Вам {age} лет");
Console.WriteLine($"Это {months} месяцев");
Console.WriteLine($"Это {decades} десятилетий");
Console.WriteLine($"В 2050 году вам будет {2050 - 2026 + age}");
}
}
Что важно запомнить
Тест: 2.5: Виды ошибок
5 вопросов