Первая программа на C++

Обычно изучение любого языка программирования начинается с написания программы, которая выводит на экран строчку "Hello World!". Поступим так же.

Итак, та самая программа:

#include <iostream>

int main()
{
    std::cout << "Hello World!\n";
}

В принципе, что здесь происходит, можно понять и чисто наугад, но давайте всё же разберём подробнее.

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

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

def main():
    print("Hello world")

if __name__ == "__main__":
    main()

Так достаточно часто пишут в реальных популярных проектах. Как пример, который первым пришёл в голову : проект Sherlock, у которого на гитхабе ~53000 звёзд. По сути, это можно назвать мимикрией под язык C++, в котором выполнение программы начинается с выполнения функции main (ну и, в принципе, им и заканчивается). 2. Перед функцией main мы написали #include <iostream> - так мы попросили препроцессор включить в наш файл весь код, записанный в библиотеке iostream. Нам это нужно, чтобы вывести текст в консоль. 3. Что же происходит в самой функции main? В ней у нас прописана только одна строчка: std::cout << "Hello world!\n";. Здесь мы помещаем в поток стандартного вывода строчку "Hello World!" с переносом на новую строчку.

Последний пункт, скорее всего, вызывает у вас больше всего вопросов. Разберём, что здесь происходит, подробнее.

  1. В этой строчке у нас записана конечная операуия. Прибавить число к переменной, вывести строчку на экран, объявить новую переменную, вызвать функцию, ... - всё это какие-то операции. В конце каждой операции в C++ нужно ставить ;. Это - очередное дизайнерское решение, отделяющее C++ от других языков, таких как, например, питон.
  2. Что такое поток стандартного вывода? Вообще, поток - это какой-то канал, через который можно отправлять куда-то информацию, или получать её оттуда. Можно представить себе это как конвейер на производстве, где на бесконечно крутящуюся ленту ставят коробки. Поток стандартного вывода - это такой же "конвейер", который поставляет определённую нами информацию в стандартный вывод (или, проще говоря, в терминал). В нашем коде мы "положили на конвейер" std::cout строчку "Hello World", и конвейер доставил эту строчку в терминал. Так же бывают строковые потоки, или файловые. В этих случаях "конвейер" будет доставлять данные в переменную string (в строчку), или в файл.
  3. Помещаем мы данные в поток при помощи оператора угловых скобок - <<. Этот оператор принимает два аргумента (один записывается слева, а другой справа). Первый аргумент - это поток, куда мы будем помещать информацию, а второй - сама "информация" (имя переменной, класс, вызов функции, ...), которую нам надо передать. Если же вызвать оператор << для двух целых чисел (например, 1 << 2), произойдёт бинарный сдвиг. Подробнее о различных операторах будем говорить в темах "основные операции C++" и "перегрузка операторов".
  4. В конце мы записали символ \n. Это - специальный символ, который обозначает переход на новую строку в терминале. Когда компьютер видит, что мы хотим вывести \n, он понимает, что мы хотим перейти на следующую строчку.

Почему переход на новую строку так странно записывается? Почему нельзя было просто нажать в коде Enter? Дело в том, что \n - это специальная управляющая последовательность, или же escape sequence для терминала.

Когда мы работаем с терминалом в нашей программе (да и в любой другой), мы работаем по сути с 2D-полем, на котором можем рисовать свои символы. На этом поле у нас есть курсор, который определяет конкретную позицию на поле, на котором мы остановились.

Итак. Вот мы вывели на экран "Hello world".

Hello world!
            @ <- так я изобразил курсор. Он указывает на поле после `!`

И дальше мы хотим перейти на следующую строчку. Вспомним, что мы находимся на 2D-поле. И во фразе "поместить на поле символ Enter" нет никакого смысла! Потому что Enter - это управляющая кнопка на клавиатуре, которая не представляет никакой символ. Символы - это буквы, цифры, знаки препинания и т.д. Чтобы перейти на следующую строчку, нам на самом деле нужно переместить курсор на начало следующей строки.

Представьте себе курсор как исполнителя-робота из Кумира, или черепаху из ПервоЛого: курсору, чтобы перейти по 2D-полю на начало следующей строки, надо:

  1. Опуститься на одну "клетку" вниз - на следующую строку
Hello world!

            @
  1. Переместиться влево до начала текущей строки
Hello world!

@

Таким образом, мы перешли на новую строку. Да, оказывается, даже переход на новую строчку в терминале не такой простой, как кажется!

...стоит понимать, что \n - это не единственная управляющая последовательность. Среди основных других можно встретить:

  • \t - вывод таба (Tab) на экран
  • \r - перемещение курсора в начало текущей строки
  • \', \", \\ - не совсем управляющие последовательности, но просто способ вывести символы ', " и \ на экран.

Таким образом, именно из-за таких особенностей работы в терминале, чтобы перейти в терминале на новую строчку, нужно писать \n. Но на самом деле есть и другие причины. Например:

  • если бы мы реально в коде нажимали Enter, он бы выглядел мягко говоря неряшливо. Что было бы, если бы мы хотели вывести, скажем, 5 переходов на новую строку?
  • по правилам синтаксиса C++, строчки у нас должны записываться на одной линии исходного кода (с некоторыми оговорками). У нас же в файле есть и форматирование, и табы, и отступы. Как тогда нам надо бы было с этим всем работать? Просто забить на все предыдущие табы и всё форматирование, и просто поверх фигачить строчки? Это бы тогда выглядело как-то так:
int main()
{
	std::cout << "Hello World!
This is multiline text



I wanted to print
";
}

И выглядело бы ужасно.

На самом деле, в C++ существуют raw string literals, которые помогают записывать примерно в такой форме текст в исходных файлах без управляющих последовательностей. Если интересно, что это, можешь загуглить. Но примерно с ними строчки можно записывать так:

int main()
{
	std::cout <<
R"###(
Hello world!
This is multiline text


I wanted to print.
)###";
}

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

$ > g++ main.cpp -o main
$ > ./main
Hello world! $ >

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

Завершение

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