Перейти к содержимому
Теория · Первая программа · урок 1 из 7

Программа и компилятор

Чему вы научитесь

Чему вы научитесь:

  • объяснять, как текст программы на C# становится работающей программой;
  • печатать текст через Console.WriteLine и Console.Write и предсказывать, где окажется каждая строка вывода;
  • читать сообщение компилятора и исправлять ошибку, которую он нашёл;
  • отличать инструкции верхнего уровня от классического метода Main.
Рецепт hello.cs с галочкой корректора становится карточкой символов IL, и машина dotnet переводит её в машинный код

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

Рецепт для компьютера пишут обычными буквами в обычном текстовом файле. Файлы с программами на C# получают расширение .cs. Вот текст целой программы — она состоит из одной строки и печатает приветствие:

C#
Console.WriteLine("Hello, World!"); // Hello, World!

Текст после // — комментарий: пометка для людей, на работу программы она не влияет. В примерах курса такой комментарий показывает, что напечатает строка, а знак ✗ в комментарии отмечает строку с ошибкой.

Есть одна сложность: процессор не читает буквы. Процессор — это часть компьютера, которая выполняет команды. Понимает он только машинный код: длинные цепочки чисел, и у каждого вида процессоров они свои. Поэтому текст программы нужно перевести, и в C# этот перевод идёт в два этапа.

Компилятор — строгий корректор

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

Корректор работает по двум правилам:

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

Здесь картинка с корректором перестаёт работать. Настоящий корректор заметил бы опечатку в слове, а компилятор следит только за правилами языка и не вникает в смысл. Если написать "Helo, World!", программа запустится, и опечатку вы увидите только в том, что она напечатает.

Промежуточный язык IL

Компилятор C# не переводит текст сразу в машинный код вашего процессора. Он переводит его в промежуточный язык IL — набор команд для воображаемого универсального процессора — и записывает результат в файл с расширением .dll.

Зачем нужен промежуточный шаг? Компьютеры бывают разными:

  • в ноутбуках чаще всего стоят процессоры Intel и AMD, а в телефонах и новых Mac — процессоры ARM, и машинный код у них разный;
  • на одних компьютерах стоит Windows, на других — Linux или macOS, а одна и та же программа должна работать на всех.

Один и тот же файл с IL запускается на любом из этих компьютеров, если на нём есть .NET — платформа Microsoft, на которой работают программы на C#. Это как рецепт, переписанный условными значками: его поймёт повар на любой кухне мира, а плита, как и процессор, по значкам готовить не умеет.

Среда выполнения .NET

Второй этап — среда выполнения .NET: программа, которая загружает файл .dll, переводит IL в машинный код именно этого процессора и запускает его. Переводит она не заранее, а прямо во время работы: каждый кусок программы — перед тем, как он понадобится впервые. Такой перевод «точно в срок» называют JIT-компиляцией.

Весь путь от текста до работающей программы выглядит так:

  1. Вы пишете текст программы в файле hello.cs.
  2. Компилятор C# проверяет текст, переводит его в IL и записывает результат в файл .dll.
  3. Среда выполнения .NET загружает этот файл и переводит IL в машинный код вашего процессора.
  4. Процессор выполняет машинный код, и программа печатает Hello, World!.

На собеседовании об этом пути часто спрашивают так: как код на C# становится работающей программой? Хороший ответ называет все четыре звена этого пути: компилятор, IL в файле .dll, JIT-компиляцию в среде выполнения и машинный код.

Запуск через dotnet run

Компилятор, среда выполнения и команда dotnet, которая управляет ими обоими, устанавливаются одним набором — .NET SDK. Чтобы запустить программу, сделайте три шага:

  1. Сохраните текст программы в файл hello.cs.
  2. Откройте окно для текстовых команд — терминал — в папке с этим файлом. В VS Code откройте эту папку и выберите в меню «Терминал» → «Создать терминал»: терминал откроется сразу в ней.
  3. Наберите команду dotnet run hello.cs.

Команда передаст файл компилятору и, если ошибок нет, сразу запустит результат в среде выполнения. Файл .dll при этом появится не рядом с hello.cs, а в отдельной папке, которую .NET заводит для таких программ. Если же компилятор нашёл ошибку, команда напечатает её и ничего не запустит.

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

В терминале набрали dotnet run и забыли имя файла:

TEXT
Couldn't find a project to run.

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

Наберите команду целиком: dotnet run hello.cs. То же сообщение появится, если терминал открыт не в той папке, где лежит hello.cs.

Главное

  • Компилятор проверяет весь текст программы до запуска: при ошибке не выполняется ни одна строка.
  • Компилятор C# переводит текст в промежуточный язык IL и записывает его в файл .dll.
  • Среда выполнения .NET переводит IL в машинный код во время работы программы — это JIT-компиляция.
  • dotnet run hello.cs компилирует файл и сразу запускает программу.