From bcd4b07f4f920e55f7bd7deab1406f93901b400a Mon Sep 17 00:00:00 2001 From: ola-9 Date: Fri, 21 Jun 2024 15:57:09 +0300 Subject: [PATCH 1/2] clean up new structure --- .../10-first-program/description.ru.yml | 42 --- .../10-basics/10-first-program/ru/data.yml | 1 + .../10-basics/20-comments/description.ru.yml | 52 ---- modules/10-basics/20-comments/ru/data.yml | 1 + .../10-basics/30-statemets/description.ru.yml | 57 ----- modules/10-basics/30-statemets/ru/data.yml | 1 + .../40-program-structure/description.ru.yml | 79 ------ .../40-program-structure/ru/data.yml | 1 + .../10-basics/50-testing/description.ru.yml | 49 ---- modules/10-basics/50-testing/ru/data.yml | 1 + .../60-syntax-error/description.ru.yml | 29 --- modules/10-basics/60-syntax-error/ru/data.yml | 1 + .../10-basics/70-compiler/description.ru.yml | 52 ---- modules/10-basics/70-compiler/ru/data.yml | 1 + .../10-basics/description.ru.yml | 57 ----- modules/20-arithmetics/10-basics/ru/data.yml | 1 + .../20-operators/description.ru.yml | 37 --- .../20-arithmetics/20-operators/ru/data.yml | 1 + .../30-commutativity/description.ru.yml | 17 -- .../30-commutativity/ru/data.yml | 1 + .../40-composition/description.ru.yml | 32 --- .../20-arithmetics/40-composition/ru/data.yml | 1 + .../50-priority/description.ru.yml | 38 --- .../20-arithmetics/50-priority/ru/data.yml | 1 + .../60-float/description.ru.yml | 35 --- modules/20-arithmetics/60-float/ru/data.yml | 1 + .../70-codestyle/description.ru.yml | 41 --- .../20-arithmetics/70-codestyle/ru/data.yml | 1 + .../10-definition/description.ru.yml | 115 --------- .../30-variables/10-definition/ru/data.yml | 1 + .../30-variables/20-change/description.ru.yml | 43 ---- modules/30-variables/20-change/ru/data.yml | 1 + .../30-variables-naming/description.ru.yml | 45 ---- .../30-variables-naming/ru/data.yml | 1 + .../30-variables/40-errors/description.ru.yml | 67 ----- modules/30-variables/40-errors/ru/data.yml | 1 + .../description.ru.yml | 75 ------ .../50-variables-expressions/ru/data.yml | 1 + .../60-naming-style/description.ru.yml | 36 --- .../30-variables/60-naming-style/ru/data.yml | 1 + .../70-magic-numbers/description.ru.yml | 66 ----- .../30-variables/70-magic-numbers/ru/data.yml | 1 + .../10-integer-types/description.ru.yml | 99 -------- .../10-integer-types/ru/data.yml | 1 + .../20-floating-type/description.ru.yml | 121 --------- .../20-floating-type/ru/data.yml | 1 + .../30-logic-type/description.ru.yml | 37 --- .../40-data-types/30-logic-type/ru/data.yml | 1 + .../40-char-type/description.ru.yml | 65 ----- .../40-data-types/40-char-type/ru/data.yml | 1 + .../50-type-casting/description.ru.yml | 101 -------- .../40-data-types/50-type-casting/ru/data.yml | 1 + .../60-type-alias/description.ru.yml | 65 ----- .../40-data-types/60-type-alias/ru/data.yml | 1 + .../70-const-type/description.ru.yml | 72 ------ .../40-data-types/70-const-type/ru/data.yml | 1 + .../80-string-type/description.ru.yml | 78 ------ .../40-data-types/80-string-type/ru/data.yml | 1 + .../description.ru.yml | 83 ------ .../10-introduction-pointers/ru/data.yml | 1 + .../20-pointers/description.ru.yml | 240 ------------------ .../20-pointers/ru/data.yml | 1 + .../10-define-function/description.ru.yml | 118 --------- .../10-define-function/ru/data.yml | 1 + .../20-function-return/description.ru.yml | 119 --------- .../20-function-return/ru/data.yml | 1 + .../description.ru.yml | 80 ------ .../30-functions-parameters/ru/data.yml | 1 + .../40-function-prototypes/description.ru.yml | 68 ----- .../40-function-prototypes/ru/data.yml | 1 + .../50-reloading-function/description.ru.yml | 92 ------- .../50-reloading-function/ru/data.yml | 1 + .../description.ru.yml | 98 ------- .../60-types-overloads-function/ru/data.yml | 1 + .../70-default-arguments/description.ru.yml | 121 --------- .../70-default-arguments/ru/data.yml | 1 + 76 files changed, 38 insertions(+), 2721 deletions(-) delete mode 100644 modules/10-basics/10-first-program/description.ru.yml delete mode 100644 modules/10-basics/20-comments/description.ru.yml delete mode 100644 modules/10-basics/30-statemets/description.ru.yml delete mode 100644 modules/10-basics/40-program-structure/description.ru.yml delete mode 100644 modules/10-basics/50-testing/description.ru.yml delete mode 100644 modules/10-basics/60-syntax-error/description.ru.yml delete mode 100644 modules/10-basics/70-compiler/description.ru.yml delete mode 100644 modules/20-arithmetics/10-basics/description.ru.yml delete mode 100644 modules/20-arithmetics/20-operators/description.ru.yml delete mode 100644 modules/20-arithmetics/30-commutativity/description.ru.yml delete mode 100644 modules/20-arithmetics/40-composition/description.ru.yml delete mode 100644 modules/20-arithmetics/50-priority/description.ru.yml delete mode 100644 modules/20-arithmetics/60-float/description.ru.yml delete mode 100644 modules/20-arithmetics/70-codestyle/description.ru.yml delete mode 100644 modules/30-variables/10-definition/description.ru.yml delete mode 100644 modules/30-variables/20-change/description.ru.yml delete mode 100644 modules/30-variables/30-variables-naming/description.ru.yml delete mode 100644 modules/30-variables/40-errors/description.ru.yml delete mode 100644 modules/30-variables/50-variables-expressions/description.ru.yml delete mode 100644 modules/30-variables/60-naming-style/description.ru.yml delete mode 100644 modules/30-variables/70-magic-numbers/description.ru.yml delete mode 100644 modules/40-data-types/10-integer-types/description.ru.yml delete mode 100644 modules/40-data-types/20-floating-type/description.ru.yml delete mode 100644 modules/40-data-types/30-logic-type/description.ru.yml delete mode 100644 modules/40-data-types/40-char-type/description.ru.yml delete mode 100644 modules/40-data-types/50-type-casting/description.ru.yml delete mode 100644 modules/40-data-types/60-type-alias/description.ru.yml delete mode 100644 modules/40-data-types/70-const-type/description.ru.yml delete mode 100644 modules/40-data-types/80-string-type/description.ru.yml delete mode 100644 modules/45-ref-and-pointers/10-introduction-pointers/description.ru.yml delete mode 100644 modules/45-ref-and-pointers/20-pointers/description.ru.yml delete mode 100644 modules/50-functions/10-define-function/description.ru.yml delete mode 100644 modules/50-functions/20-function-return/description.ru.yml delete mode 100644 modules/50-functions/30-functions-parameters/description.ru.yml delete mode 100644 modules/50-functions/40-function-prototypes/description.ru.yml delete mode 100644 modules/50-functions/50-reloading-function/description.ru.yml delete mode 100644 modules/50-functions/60-types-overloads-function/description.ru.yml delete mode 100644 modules/50-functions/70-default-arguments/description.ru.yml diff --git a/modules/10-basics/10-first-program/description.ru.yml b/modules/10-basics/10-first-program/description.ru.yml deleted file mode 100644 index 5fd094d..0000000 --- a/modules/10-basics/10-first-program/description.ru.yml +++ /dev/null @@ -1,42 +0,0 @@ ---- - -name: Первая программа на С++ -theory: | - Изучать язык программирования, по традиции, начинают с программы 'Hello, World!'. - -
-    Hello, World!
-  
- - В языке C++ эта программа будет выглядеть так: - - ```cpp - main() { - std::cout << "Hello, World!"; - } - ``` - - Текст `Hello, World!` появится на экране благодаря команде `std::cout <<`. - Такая команда выводит на экран информацию, которая указана после оператора << `'Hello, World!'`. - Оператор `<<` позволяет строить цепочки, например: `std::cout << "Hello, World!" << "\n";` - То есть теперь информация после _Hello, World!_ будет выводиться на экране на следующей строке. - - То, что присутствует на экране помимо этой команды, нужно для работы любой программы на языке C++, мы разберём это позднее. -instructions: | - Наберите в редакторе код из задания символ в символ и нажмите «Проверить». - - ```cpp - #include - - int main() { - //BEGIN - std::cout << "Hello, World!" << std::endl; - //END - return 0; - } - ``` - - > Внимание: если вы напишете `heLLo, woRld!` вместо `Hello, World!`, то это будет считаться другим текстом, потому что заглавные и строчные буквы — это разные символы. Размер буквы называют *регистром*, и говорят: **регистр — важен!** Это касается почти всего в коде, поэтому привыкайте всегда обращать внимание на регистр. - -tips: - - "Если в редакторе есть запись `// BEGIN` и `// END`, то код нужно писать между этими строчками." diff --git a/modules/10-basics/10-first-program/ru/data.yml b/modules/10-basics/10-first-program/ru/data.yml index 2f4a497..c2c8643 100644 --- a/modules/10-basics/10-first-program/ru/data.yml +++ b/modules/10-basics/10-first-program/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Первая программа на С++ tips: - >- diff --git a/modules/10-basics/20-comments/description.ru.yml b/modules/10-basics/20-comments/description.ru.yml deleted file mode 100644 index 0e0bcfa..0000000 --- a/modules/10-basics/20-comments/description.ru.yml +++ /dev/null @@ -1,52 +0,0 @@ ---- - -name: Комментарии в С++ -theory: | - - Кроме кода, в файлах с исходным кодом могут находиться комментарии. Это текст, который не является частью программы и нужен программистам для пометок. С их помощью добавляют пояснения, как работает код, какие здесь ошибки нужно поправить или не забыть что-то добавить позже. - - ```cpp - int main() { - // Удалить вывод в консоль перед запуском в продакшене - std::cout << "Hello World!"; - return 0; - } - ``` - - Комментарии в С++ бывают двух видов: - ## Однострочные комментарии - _Однострочные комментарии_ начинаются с `//`. После этих двух символов может следовать любой текст, вся строчка не будет анализироваться и исполняться. - Комментарий может занимать всю строчку. Если одной строчки мало, то создаются несколько комментариев: - - ```cpp - // For Winterfell! - // For Lanisters! - ``` - - Комментарий может находиться на строчке после какого-нибудь кода: - - ```cpp - int index { 0 }; // counter initialization - ``` - ## Многострочные комментарии - _Многострочные комментарии_ начинаются с `/*` и заканчиваются на `*/`. - - ```cpp - /* - This program - prints a message - to the console - */ - int main() { - std::cout << "Hello World!" << std::endl; - return 0; - } - ``` - - Такие комментарии, обычно, используют для документирования кода, например, функций. -instructions: | - Выведете в консоль строку `Hello Jon Snow!` - - В любом месте программы создайте однострочный комментарий с текстом: `You know nothing, Jon Snow!`. - - Не забудьте добавить перевод строки с помощью функции `std::endl` или символа `\n` diff --git a/modules/10-basics/20-comments/ru/data.yml b/modules/10-basics/20-comments/ru/data.yml index 8b6c1aa..f12e496 100644 --- a/modules/10-basics/20-comments/ru/data.yml +++ b/modules/10-basics/20-comments/ru/data.yml @@ -1 +1,2 @@ +--- name: Комментарии в С++ diff --git a/modules/10-basics/30-statemets/description.ru.yml b/modules/10-basics/30-statemets/description.ru.yml deleted file mode 100644 index 4ad8743..0000000 --- a/modules/10-basics/30-statemets/description.ru.yml +++ /dev/null @@ -1,57 +0,0 @@ ---- -name: Инструкции (Statements) -theory: | - Инструкция — это команда для компьютера. Код на C++ — это набор инструкций, которые, обычно, отделяются друг от друга символом `;`. Вот пример кода с двумя инструкциями: - - ```cpp - std::cout << "Mother of Dragons\n"; - std::cout << "Dracarys!\n"; - ``` - - При запуске этого кода на экран последовательно выводятся два предложения: - -
-  Mother of Dragons
-  Dracarys!
-  
- - Почему это важно знать? Инструкция — это единица исполнения. Программа которая запускает код на C++, выполняет инструкции строго по очереди. И мы, как разработчики, должны понимать этот порядок и уметь мысленно разделять программу на независимые части, удобные для анализа. - Теоретически инструкции можно написать последовательно друг за другом без переноса на новую строчку: - - ```cpp - std::cout << "Mother of Dragons\n"; std::cout << "Dracarys!\n"; - ``` - - Результат на экране будет таким же, но на практике такой подход считается плохим. - -instructions: | - Выведите на экран друг за другом три имени: *Robert*, *Stannis*, *Renly*. В результате на экране должно отобразиться: - -
-  Robert
-  Stannis
-  Renly
-  
- - Для каждого имени можете используйте свой собственный вызов `std::cout <<`. - - Вывод в поток `std::cout` не умеет самостоятельно делать перевод строки, по этому это надо указать явно управляющим символом "\n" или воспользоваться функцией `endl`. - - ```cpp - std::cout << "Mother of Dragons\n"; - std::cout << "Mother of Dragons" << "\n"; - std::cout << "Mother of Dragons" << std::endl; - ``` - - Подсказка: - - С помощью оператора вставки `<<` можно выстраивать цепочки: - - ```cpp - std::cout << "Mother of Dragons\n" << "Dracarys!\n"; - ``` - -tips: - - | - [Функция endl](https://learn.microsoft.com/ru-ru/cpp/standard-library/ostream-functions?view=msvc-170#endl) - [Что такое поток](https://learn.microsoft.com/ru-ru/cpp/standard-library/what-a-stream-is?view=msvc-170) - [Использование printf в современном С++](https://learn.microsoft.com/ru-ru/archive/msdn-magazine/2015/march/windows-with-c-using-printf-with-modern-c) diff --git a/modules/10-basics/30-statemets/ru/data.yml b/modules/10-basics/30-statemets/ru/data.yml index 58b2947..1156db0 100644 --- a/modules/10-basics/30-statemets/ru/data.yml +++ b/modules/10-basics/30-statemets/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Инструкции (Statements) tips: - > diff --git a/modules/10-basics/40-program-structure/description.ru.yml b/modules/10-basics/40-program-structure/description.ru.yml deleted file mode 100644 index f60b454..0000000 --- a/modules/10-basics/40-program-structure/description.ru.yml +++ /dev/null @@ -1,79 +0,0 @@ ---- - -name: Структура программы в С++ -theory: | - Давайте посмотрим на листинг программы из прошлого урока. - - ```cpp - #include - - int main() { - std::cout << "Mother of Dragons\n"; - std::cout << "Dracarys!\n"; - return 0; - } - ``` - - В верхней части листинга мы видим такую запись: - - ```cpp - #include - ``` - - `#include` - это директива препроцессора, с помощью неё мы подключаем заголовочные файлы различных библиотек. В данном случае мы подключили `iostream` - библиотека для организации ввода/вывода. - - Далее идет определение функции main. В С++ любая программа начинается с функции `main` - это её входная точка. - - Функция может принимать аргументы, например, если это консольное приложение, то это будут аргументы командной строки: - - ```cpp - int main (int argc, char *argv[]) { - // some code - } - ``` - - или же быть без аргументов: - - ```cpp - int main() { - // some code - } - ``` - - Тело функции обрамлено фигурными скобками, внутри идет набор инструкций, которые выполняет функция, каждая инструкция должна оканчиваться точкой с запятой ;. - - ## Код возврата - - Когда наша программа завершает свою работу, операционной системе важно понимать как отработала программа: успешно или нет. Рассмотрим минимальное определение функции `main`: - - ```cpp - int main() { - return 0; - } - ``` - - Обратите внимание на `return` - это инструкция возврата из функции(подробнее о возвращаемых значениях мы познакомимся в уроках про функции). - - Стандарт C++ определяет значение только трех кодов состояния: `0`, `EXIT_SUCCESS` и `EXIT_FAILURE`. `0` и `EXIT_SUCCESS` означают, что программа выполнена успешно. `EXIT_FAILURE` означает, что программа не была успешно выполнена. `EXIT_SUCCESS` и `EXIT_FAILURE` определены в заголовочном файле ``: - - ```cpp - #include - - int main() { - return EXIT_SUCCESS // эквивалентно 0 - } - ``` - - Если вы хотите максимизировать портируемость, вы должны использовать только `0` или `EXIT_SUCCESS`. - - -instructions: | - В этом упражнении мы снова потренируем вывод в консоль, только теперь вы напишете функцию `main` самостоятельно. - - Не забудьте подключить библиотеку для ввода/вывода. - - Выведите в консоль фразу Hello, Code Basics! - -tips: - - | - [Директива #include](https://learn.microsoft.com/ru-ru/cpp/preprocessor/hash-include-directive-c-cpp?view=msvc-170) diff --git a/modules/10-basics/40-program-structure/ru/data.yml b/modules/10-basics/40-program-structure/ru/data.yml index 5af5bdf..6cc9893 100644 --- a/modules/10-basics/40-program-structure/ru/data.yml +++ b/modules/10-basics/40-program-structure/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Структура программы в С++ tips: - > diff --git a/modules/10-basics/50-testing/description.ru.yml b/modules/10-basics/50-testing/description.ru.yml deleted file mode 100644 index 79232ca..0000000 --- a/modules/10-basics/50-testing/description.ru.yml +++ /dev/null @@ -1,49 +0,0 @@ ---- -name: Как мы проверяем ваши решения -theory: | - Наш сайт автоматически проверяет ваши решения. Как это работает? - - В самом простом случае система просто запускает ваш код и смотрит на то, что вывелось на экран. А потом сверяет с тем, что мы «ожидали» по заданию. - - В следующих, более сложных уроках вы будете писать методы — некие мини-программы, принимающие информацию из внешнего мира и производящие какие-то операции. Проверка ваших решений в таких случаях выглядит немного сложнее: система запускает ваше решение и передаёт какую-то информацию. Система также знает — «ожидает» — какой именно ответ должен вернуть правильный метод при таких входных данных. - - Например, если ваша задача — написать код для сложения двух чисел, то проверочная система будет передавать ему разные комбинации чисел и сверять ответ вашего кода с реальными суммами. Если во всех случаях ответы совпадут, то решение считается верным. - - Вот простой пример: помните задание из 1го упражнения, где вам надо было вывести на экран "Hello, World!"?. Допустим, вы допустили небольшую опечатку. Система ответит примерно так: - -
-  Assertion `result == expected` failed in test.cpp line 26: 
-  Expected: "Hello, World" to be: "Hello, World!"
-  
- - Читать это следует так: в ответе ожидалось строка с "Hello, World!" (первая строчка после двойного '='), но в вместо него пришла строка "Hello, World". Строки отличаются символом под номером 12. В данном примере пропущен восклицательный знак. - - Иногда в процессе решения будет казаться, что вы сделали все правильно, но система "капризничает" и не принимает решение. Подобное поведение практически исключено. Нерабочие тесты просто не могут попасть на сайт, они автоматически запускаются после каждого изменения. В подавляющем большинстве таких случаев, (а все наши проекты в сумме провели миллионы проверок за много лет), ошибка содержится в коде решения. Она может быть очень незаметной, вместо английской буквы случайно ввели русскую, вместо верхнего регистра использовали нижний или забыли вывести запятую. Другие случаи сложнее. Возможно ваше решение работает для одного набора входных данных, но не работает для другого. Поэтому всегда внимательно читайте условие задачи и вывод тестов. Там почти наверняка есть указание на ошибку. - - Однако, если вы уверены в ошибке или нашли какую-то неточность, то вы всегда можете указать на нее. В конце каждой теории есть ссылка на содержимое урока на гитхабе (этот проект полностью открытый!). Перейдя туда, вы можете написать issue, посмотреть содержимое тестов (там видно, как вызывается ваш код) и даже отправить pullrequest. Если для вас это пока темный лес, то подключитесь в наше сообщество [Telegram Hexlet](https://t.me/hexletcommunity/12), там в канале Волонтеры мы всегда поможем. - - Кроме наших тестов, будет крайне полезно экспериментировать с кодом в вашем собственном консольном приложении. [Установите Visual Studio](https://visualstudio.microsoft.com/ru/downloads/) и попробуйте создать [простое консольное приложение](https://learn.microsoft.com/ru-ru/cpp/build/vscpp-step-1-create?view=msvc-170). Оцените мощь IDE, которая может сама автодополнять выражения и подчеркивать места с ошибками. - - Если вы используете Linux, то у вас уже установлен компилятор g++ и вы можете скомпилировать программу с помощью него, а код набирать в любом текстовом редакторе, например [VScode](https://code.visualstudio.com/). - -instructions: | - Просто тренировка. Выведите на экран число 420262531. - -
-  420262531
-  
- - Поэкспериментируйте с выводом. Передайте туда другое число или строку. Посмотрите на ответ системы, попробуйте его перевести и понять. - -definitions: - - name: Тесты - description: | - специальный код, проверяющий программы на корректность, сверяя правильный результат с реальным. - -tips: - - | - [g++](https://gcc.gnu.org/onlinedocs) - - | - [TDD](https://ru.wikipedia.org/wiki/Разработка_через_тестирование) - - | - [Сообщество Хекслета в Telegram](https://t.me/hexletcommunity/12) diff --git a/modules/10-basics/50-testing/ru/data.yml b/modules/10-basics/50-testing/ru/data.yml index f98ffba..87f69da 100644 --- a/modules/10-basics/50-testing/ru/data.yml +++ b/modules/10-basics/50-testing/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Как мы проверяем ваши решения definitions: - name: Тесты diff --git a/modules/10-basics/60-syntax-error/description.ru.yml b/modules/10-basics/60-syntax-error/description.ru.yml deleted file mode 100644 index eec8ca1..0000000 --- a/modules/10-basics/60-syntax-error/description.ru.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: Ошибки оформления (синтаксиса) -theory: | - В человеческих языках грамматика важна, но текст с ошибками чаще всего можно понять и прочитать. В программировании всё строго. Любое мельчайшее нарушение, и программа не запустится. Примером может быть забытая `;`, неправильно расставленные скобки и другие детали. Подобные ошибки называются синтаксическими, потому что они нарушают правила синтаксиса языка. Если программа на C++ написана синтаксически некорректно, то компилятор выводит на экран соответствующее сообщение, а также указание на файл и строчку в нём, где по его мнению произошла ошибка. Ниже пример кода с синтаксической ошибкой: - - ```cpp - std::cout << "alala - ``` - - Если попробовать запустить код выше, то мы увидим следующее сообщение: - -
-  ./main.cpp:5:16: error: expected expression
-  std::cout << "alala
-               ^
-  1 error generated.
-  
- - С одной стороны, ошибки синтаксиса — самые простые, потому что они связаны исключительно с грамматическими правилами написания кода, а не с самим смыслом кода. Их легко исправить: нужно лишь найти нарушение в записи. С другой стороны, компилятор не всегда может чётко указать на это нарушение. Поэтому бывает, что забытую скобку нужно поставить не туда, куда указывает сообщение об ошибке. - -instructions: | - Это задание не связано с уроком напрямую. Но будет полезным потренироваться с выводом на экран. Выведите на экран *What Is Dead May Never Die*. - -definitions: - - name: Компилятор - description: Программа выполняющая преобразование исходного кода в низкоуровневый машинный код подходящий для выполнения - - - name: Синтаксическая ошибка - description: Нарушение грамматических правил языка программирования diff --git a/modules/10-basics/60-syntax-error/ru/data.yml b/modules/10-basics/60-syntax-error/ru/data.yml index 1f501cb..2cc434b 100644 --- a/modules/10-basics/60-syntax-error/ru/data.yml +++ b/modules/10-basics/60-syntax-error/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Ошибки оформления (синтаксиса) definitions: - name: Компилятор diff --git a/modules/10-basics/70-compiler/description.ru.yml b/modules/10-basics/70-compiler/description.ru.yml deleted file mode 100644 index 436e0ad..0000000 --- a/modules/10-basics/70-compiler/description.ru.yml +++ /dev/null @@ -1,52 +0,0 @@ ---- - -name: Компиляция и компоновка (линковка) -theory: | - С++ — компилируемый язык. Поэтому прежде чем запускать написанную программу, нам нужно превратить текстовые файлы с исходным кодом в машинный код, который понятен компьютеру. - - В этом уроке мы узнаем, что такое компилятор. Также разберем, как происходит процесс компиляции, который можно разбить на две стадии: **компиляция** и **компоновка**. - - ## Компиляция - - Чтобы скомпилировать программу на C++, мы используем специальную программу. Она последовательно просматривает каждый файл исходного кода (.cpp) и выполняет две важные задачи: - - 1. Проверяет код на соответствие правилам языка C++. В противном случае компилятор выдаст ошибку и номер соответствующей строки, чтобы помочь точно определить, что нужно исправить. Процесс компиляции будет прерван, пока ошибка не будет исправлена - - 2. Переводит исходный код C++ в файл машинного кода, называемый объектным файлом - - Объектные файлы обычно имеют имена `name.o` или `name.obj`, где name совпадает с именем файла `.cpp`, из которого он был создан. - - Если бы в вашей программе было бы три файла `.cpp`, компилятор сгенерировал бы три объектных файла. - - Компиляторы C++ доступны для многих операционных систем. Например, в стандартной поставке многих дистрибутивов Linux есть компилятор **gcc**. В Windows можно пользоваться IDE Visual Studio — в нее уже встроен компилятор и система сборки. - - ## Компоновка - - После того, как компилятор создал один или несколько объектных файлов, включается другая программа — **компоновщик** или **линкер**. Работа компоновщика состоит из трех частей: - - 1. Берет все объектные файлы, сгенерированные компилятором, и объединяет их в единую исполняемую программу - - 2. Помимо возможности связывать объектные файлы компоновщик также может связывать файлы библиотек. Файл библиотеки — это набор предварительно скомпилированного кода, который был упакован для повторного использования в других программах - - 3. Обеспечивает правильное разрешение всех межфайловых зависимостей. Например, если мы определяем что-то в одном файле `.cpp`, а затем используем это в другом файле `.cpp`, компоновщик соединит их вместе. Если компоновщик не может связать ссылку с чем-то с ее определением, мы получим ошибку компоновщика, и процесс линковки прервется - - ## Системы сборки - - Когда проект содержит десятки и даже сотни файлов с исходным кодом, процесс его сборки надо автоматизировать. Здесь на помощь приходят системы сборки, которые автоматически запускают все нужные команды, чтобы скомпилировать и скомпоновать все файлы проекта. В итоге на выходе получается один исполняемый файл. - - Одной из таких систем является утилита `Make` и `Makefile`. В `Makefile` описываются все цели и зависимости проекта, а утилита `Make` смотрит в этот файл и запускает компилятор с соответствующими командами. - - Еще одна популярная система сборки проектов — утилита `CMake`, которая работает поверх `Make`. Она отличается своей кроссплатформенностью и позволяет делать сборки под различные операционные системы. - - Компиляции и сборка программы не менее важный процесс, чем написание самой программы. - -instructions: | - Выведите на экран число 9780262531962. - -tips: - - | - "Если в редакторе есть запись `// BEGIN` и `// END`, то код нужно писать между этими строчками." - - | - [Установка gcc в Windows](https://www.digitalocean.com/community/tutorials/c-compiler-windows-gcc) - - | - [gcc для Linux](https://gcc.gnu.org/onlinedocs) diff --git a/modules/10-basics/70-compiler/ru/data.yml b/modules/10-basics/70-compiler/ru/data.yml index eabbe8a..9a88a2a 100644 --- a/modules/10-basics/70-compiler/ru/data.yml +++ b/modules/10-basics/70-compiler/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Компиляция и компоновка (линковка) tips: - > diff --git a/modules/20-arithmetics/10-basics/description.ru.yml b/modules/20-arithmetics/10-basics/description.ru.yml deleted file mode 100644 index 36e86b0..0000000 --- a/modules/20-arithmetics/10-basics/description.ru.yml +++ /dev/null @@ -1,57 +0,0 @@ ---- -name: Арифметические операции -theory: | - На базовом уровне компьютеры оперируют только числами. Даже в прикладных программах на высокоуровневых языках внутри много чисел и операций над ними. К счастью, для старта достаточно знать обычную арифметику — с нее и начнем. - - Для сложения двух чисел в математике мы пишем, например, *3 + 4*. В программировании — то же самое. Вот программа, складывающая два числа: - - ```cpp - // Не забываем точку с запятой в конце, - // так как каждая строчка в коде - инструкция - int main() { - 3 + 4; - return 0; - } - ``` - - Если запустить эту программу на выполнение, то она тихо отработает и завершиться. На экран ничего не будет выведено. Операция сложения, как и остальные операции, сама по себе ничего не делает кроме сложения. Чтобы воспользоваться результатом сложения, его нужно, например, вывести на экран. - - ```cpp - int main() { - std::cout << 3 + 4; - return 0; - } - ``` - - После запуска на экране появится результат: - -
7
- - Кроме сложения доступны следующие операции: - * `*` — умножение - * `/` — деление - * `-` — вычитание - * `%` — [остаток от деления](https://ru.wikipedia.org/wiki/Деление_с_остатком) - - Теперь давайте выведем на экран результат деления, а потом результат возведения в степень: - - ```cpp - int main() { - std::cout << 8 / 2; - std::cout << 3 * 3 * 3; - } - ``` - -
-  4
-  27
-  
- -instructions: | - Выведите на экран результат деления числа *81* на *9*. Не забудьте сделать перевод строки. - -tips: - - | - Всегда отбивайте арифметические операторы пробелами от самих чисел (операндов) – это хороший стиль программирования. Поэтому в наших примерах `std::cout << 3 + 4`, а не `std::cout << 3+4`. - - | - Остаток от деления отбрасывается с округлением вниз. Таким образом результат деления – всегда целое число. Как работать с вещественными числами будет показано в следующих уроках. diff --git a/modules/20-arithmetics/10-basics/ru/data.yml b/modules/20-arithmetics/10-basics/ru/data.yml index 929aa9f..bbd9fb5 100644 --- a/modules/20-arithmetics/10-basics/ru/data.yml +++ b/modules/20-arithmetics/10-basics/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Арифметические операции tips: - > diff --git a/modules/20-arithmetics/20-operators/description.ru.yml b/modules/20-arithmetics/20-operators/description.ru.yml deleted file mode 100644 index c3f8423..0000000 --- a/modules/20-arithmetics/20-operators/description.ru.yml +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: Операторы -theory: | - Перед тем, как двигаться дальше, разберем базовую терминологию. Знак операции, такой как `+`, называют **оператором**. Оператор — просто символ, который выполняет операцию, например сложение. - - ```cpp - std::cout << (8 + 2); - ``` - - В этом примере `+` это оператор, а числа *8* и *2* — это **операнды**. Скобки конечно можно и опустить, но так выразительнее. - - В случае сложения у нас есть два операнда: один слева, другой справа от знака *+*. Операции, которые требуют наличия двух операндов, называются **бинарными**. Если пропустить хотя бы один операнд, например, так `3 + ;`, то программа завершится с синтаксической ошибкой. - - Операции (не операторы) бывают не только бинарными, но и унарными (с одним операндом), и даже тернарными (с тремя операндами)! Причем операторы могут выглядеть одинаково, но обозначать разные операции. - - ```cpp - std::cout << (-3); // => -3 - ``` - - Выше пример применения унарной операции к числу *3*. Оператор минус перед тройкой говорит компилятору взять число *3* и найти противоположное, то есть *-3*. - - Это немного может сбить с толку, потому что *-3* — это одновременно и число само по себе, и оператор с операндом, но у языков программирования такая структура. - -instructions: | - Напишите программу, которая посчитает разность между числами `6` и `-81` и выведет ответ на экран. Не забудьте сделать перевод строки. - -definitions: - - name: "Арифметическая операция" - description: "сложение, вычитание, умножение и деление." - - name: "Оператор" - description: "специальный символ, создающий операцию. Например, `+` создает операцию сложения." - - name: "Операнд" - description: "объект, который участвует в операции. `3 * 6`: здесь 3 и 6 — операнды." - - name: "Унарная операция" - description: "операция с одним операндом. Например, `-3` — унарная операция для получения числа, противоположного числу три." - - name: "Бинарная операция" - description: "операция с двумя операндами. Например, `3 + 9`." diff --git a/modules/20-arithmetics/20-operators/ru/data.yml b/modules/20-arithmetics/20-operators/ru/data.yml index e0e8a5c..6521cbd 100644 --- a/modules/20-arithmetics/20-operators/ru/data.yml +++ b/modules/20-arithmetics/20-operators/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Операторы definitions: - name: Арифметическая операция diff --git a/modules/20-arithmetics/30-commutativity/description.ru.yml b/modules/20-arithmetics/30-commutativity/description.ru.yml deleted file mode 100644 index 9a84bd0..0000000 --- a/modules/20-arithmetics/30-commutativity/description.ru.yml +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: Коммутативная операция -theory: | - Мы все помним со школы: «от перемены мест слагаемых сумма не меняется». Это один из базовых и интуитивно понятных законов арифметики, он называется **коммутативным законом**. - - Бинарная операция считается коммутативной, если, поменяв местами операнды, вы получаете тот же самый результат. Очевидно, что сложение — коммутативная операция: *3 + 2 = 2 + 3*. А вот является ли коммутативной операция вычитания? Нет: *2 - 3 ≠ 3 - 2*. В программировании этот закон работает точно так же, как в арифметике. Более того, большинство операций, с которыми мы будем сталкиваться в реальной жизни, не являются коммутативными. Отсюда вывод: всегда обращайте внимание на порядок того, с чем работаете. - -instructions: | - Это задание напрямую не связано с темой урока. Но будет полезным попрактиковаться с арифметическими операциями и выводом на экран. - - Напишите программу, которая считает и последовательно выводит на экран значения следующих математических выражений: «3 умножить на 5» и «-8 разделить на -4». - - Не забывайте что `std::cout` не умеет сам делать перевод строки. - -definitions: - - name: "Коммутативность" - description: "свойство операции, когда изменения порядка операндов не влияет на результат. Например, сложение — коммутативная операция: от перемены мест слагаемых сумма не меняется." diff --git a/modules/20-arithmetics/30-commutativity/ru/data.yml b/modules/20-arithmetics/30-commutativity/ru/data.yml index 4a856c1..669d02f 100644 --- a/modules/20-arithmetics/30-commutativity/ru/data.yml +++ b/modules/20-arithmetics/30-commutativity/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Коммутативная операция definitions: - name: Коммутативность diff --git a/modules/20-arithmetics/40-composition/description.ru.yml b/modules/20-arithmetics/40-composition/description.ru.yml deleted file mode 100644 index 325ee6e..0000000 --- a/modules/20-arithmetics/40-composition/description.ru.yml +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Композиция операций -theory: | - А что, если понадобится вычислить такое выражение: *3 + 5 - 2*? Именно так мы и запишем: - - ```cpp - std::cout << (3 + 5 - 2); // 3 + 5 - 2 => 8 - 2 => 6 - ``` - - Обратите внимание, что компьютер производит арифметические вычисления в правильном порядке: сначала деление и умножение, потом сложение и вычитание. Иногда этот порядок нужно изменить — об этом следующий урок. - - Или другой пример: - - ```cpp - std::cout << (2 * 4 * 5 * 10); - // 2 * 4 * 5 * 10 => 8 * 5 * 10 => 40 * 10 => 400 - ``` - - Как видно, операции можно соединять друг с другом, получая возможность вычислять все более сложные составные выражения. Чтобы представить себе то, как программа производит вычисления, давайте разберем пример: `2 * 4 * 5 * 10`. - - 1. Сначала вычисляется *2 * 4* и получается выражение *8 * 5 * 10*. - 2. Затем *8 * 5*. В итоге имеем *40 * 10*. - 3. В конце концов происходит последнее умножение, и получается результат *400*. - - Операции можно соединять друг с другом, получая возможность вычислять все более сложные составные выражения. - -instructions: | - Реализуйте программу, которая вычисляет значение выражения `8 / 2 + 5 - -3 / 2`. Не вычисляйте ничего самостоятельно, ваша программа должна производить все вычисления сама. - - Обратите внимание, что программа производит арифметические вычисления в правильном порядке: сначала деление и умножение, потом сложение и вычитание. Иногда этот порядок нужно изменить — об этом следующий урок. - - Также обратите внимание на то, что в C++ по умолчанию используется целочисленное деление, `3 / 2` будет `1`. В выводе не забудьте сделать перевод строки. diff --git a/modules/20-arithmetics/40-composition/ru/data.yml b/modules/20-arithmetics/40-composition/ru/data.yml index ea206ad..f721c13 100644 --- a/modules/20-arithmetics/40-composition/ru/data.yml +++ b/modules/20-arithmetics/40-composition/ru/data.yml @@ -1 +1,2 @@ +--- name: Композиция операций diff --git a/modules/20-arithmetics/50-priority/description.ru.yml b/modules/20-arithmetics/50-priority/description.ru.yml deleted file mode 100644 index 3b069c1..0000000 --- a/modules/20-arithmetics/50-priority/description.ru.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Приоритет операций -theory: | - Посмотрите внимательно на выражение *2 + 2 * 2* и посчитайте в уме ответ. - - Правильный ответ: *6*. - - Если у вас получилось *8*, то этот урок для вас. В школьной математике мы изучали понятие «приоритет операции». Приоритет определяет то, в какой последовательности должны выполняться операции. Например, умножение и деление имеют больший приоритет, чем сложение и вычитание: *2 + 3 * 2* вычислится в *8*. - - Но нередко вычисления должны происходить в порядке, отличном от стандартного приоритета. В сложных ситуациях приоритет можно (и нужно) задавать круглыми скобками, точно так же, как в школе, например: `(2 + 2) * 2`. - - Скобки можно ставить вокруг любой операции. Они могут вкладываться друг в друга сколько угодно раз. Вот пара примеров: - - ```cpp - std::cout << (3 * (4 - 2)); // => 6 - std::cout << (7 * 3 + (4 / 2) - (8 + (2 - 1))); // => 14 - ``` - - Иногда выражение сложно воспринимать визуально. Тогда можно расставить скобки, не повлияв на приоритет. Например, задание из прошлого урока можно сделать немного понятнее, если расставить скобки. - - Было: - - ```cpp - std::cout << (8 / 2 + 5 - -3 / 2); // => 10 - ``` - - Стало: - - ```cpp - std::cout << (((8 / 2) + 5) - (-3 / 2)); // => 10 - ``` - - Запомните: код пишется для людей, потому что код будут читать люди, а машины будут только исполнять его. Для машин нет «более» понятного или «менее» понятного кода, независимо от того, является ли код корректным или нет. - -instructions: | - Дано выражение `70 * 3 + 4 / 8 + 2`. - - Расставьте скобки так, чтобы оба сложения (`3 + 4` и `8 + 2`) высчитывались в первую очередь. Выведите результат на экран. Не забудьте сделать перевод строки. diff --git a/modules/20-arithmetics/50-priority/ru/data.yml b/modules/20-arithmetics/50-priority/ru/data.yml index 8b35617..da15a1d 100644 --- a/modules/20-arithmetics/50-priority/ru/data.yml +++ b/modules/20-arithmetics/50-priority/ru/data.yml @@ -1 +1,2 @@ +--- name: Приоритет операций diff --git a/modules/20-arithmetics/60-float/description.ru.yml b/modules/20-arithmetics/60-float/description.ru.yml deleted file mode 100644 index d0325a1..0000000 --- a/modules/20-arithmetics/60-float/description.ru.yml +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: Числа с плавающей точкой - -theory: | - В математике существуют разные виды чисел, например, натуральные – это целые числа от одного и больше, или рациональные – это числа с точкой, например 0.5. С точки зрения устройства компьютеров, между этими видами чисел пропасть. Попробуйте ответить на простой вопрос, сколько будет *0.2 + 0.1*? А теперь посмотрим, что на это скажет C++: - - ```cpp - std::cout << (0.2 + 0.1); - // => 0.30000000000000004 - ``` - - Операция сложения двух рациональных чисел внезапно привела к неточному вычислению результата. Тот же самый результат выдадут и другие языки программирования. Такое поведение обуславливается ограничениями вычислительных мощностей. Объем памяти, в отличие от чисел, конечен (бесконечное количество чисел требует бесконечного количества памяти для своего хранения). И если с натуральными числами эта проблема решается простым ограничением по верхней границе (есть некоторое максимальное число, которое можно ввести), то с рациональными такой финт не пройдет. - - ```cpp - #include - #include - - int main() { - std::cout << INT_MAX; // => 2147483647 - std::cout << INT_MIN; // => -32767 - } - ``` - Заголовочный файл climits определяет константы с ограничениями целочисленного типа данных для конкретной системы и компилятора. - - Рациональные числа не выстроены в непрерывную цепочку, между *0.1* и *0.2* бесконечное множество чисел. Соответственно возникает серьезная проблема, а как хранить рациональные числа? Это интересный вопрос сам по себе. В интернете множество статей, посвященных организации памяти в таких случаях. Более того, существует стандарт, в котором описано, как это делать правильно, и подавляющее число языков на него опирается. - - Для нас, как для разработчиков, важно понимать, что операции с плавающими числами неточны (эту точность можно регулировать), а значит при решении задач, связанных с подобными числами, необходимо прибегать к специальным трюкам, которые позволяют добиться необходимой точности. - -instructions: | - Вычислите и выведите на экран произведение двух чисел: *0.39* и *0.22*. Не забудьте сделать перевод строки. - -tips: - - | - [Что нужно знать про арифметику с плавающей запятой](https://habr.com/post/112953/) - [Integer limits](https://learn.microsoft.com/en-us/cpp/c-language/cpp-integer-limits?view=msvc-170) diff --git a/modules/20-arithmetics/60-float/ru/data.yml b/modules/20-arithmetics/60-float/ru/data.yml index df280e1..d888c33 100644 --- a/modules/20-arithmetics/60-float/ru/data.yml +++ b/modules/20-arithmetics/60-float/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Числа с плавающей точкой tips: - > diff --git a/modules/20-arithmetics/70-codestyle/description.ru.yml b/modules/20-arithmetics/70-codestyle/description.ru.yml deleted file mode 100644 index 38b410d..0000000 --- a/modules/20-arithmetics/70-codestyle/description.ru.yml +++ /dev/null @@ -1,41 +0,0 @@ ---- -name: Оформление кода -theory: | - Теперь, когда мы уже научились писать простые программы, можно немного поговорить о том, как их писать. - - Код программы следует оформлять определенным образом, чтобы он был достаточно понятным и простым в поддержке. Специальные наборы правил — стандарты — описывают различные аспекты написания кода. Конкретно в C++ самым распространенным стандартом является стандарт от [Google](https://google.github.io/styleguide/cppguide.html). - - В любом языке программирования существуют утилиты — так называемые **линтеры**. Они проверяют код на соответствие стандартам, и могут сами поправить небольшие отклонения от стандарта. В мире C++ есть два распространенных линтера *[cpplint](https://github.com/cpplint/cpplint)* и *[cppcheck](https://cppcheck.sourceforge.io/)*. - - Cpplint следует стандарту от Google. - - Взгляните на пример: - - ```cpp - int main () { - - } - ``` - - *cpplint* будет «ругаться» на нарушение: - - ./modules/20-arithmetics/70-codestyle/main.cpp:3: Extra space before ( in function call [whitespace/parens] [4] - - *./modules/20-arithmetics/70-codestyle/main.cpp:3* – это путь к файлу и номер строки в которой было нарушение. Далее идет текст сообщения и *[whitespace/parens]* – правило, которое было нарушено. Это правило требует отсутствие пробелов между именем функции и параметрами. Оно не влияет на результат, но помогает писать код понятнее и проще для восприятия. Код с учетом этого правила выглядит так: - - ```cpp - int main() { - - } - ``` - - Теперь линтер ругаться не будет. - - Какой мы делаем вывод? Линтер это хорошо, но он не отменяет самостоятельного анализа и упрощения чтения кода. - - Сейчас сайт не будет проверять ваш код линтером, но в ваших будущих практиках на [Хекслете](https://ru.hexlet.io) и в реальной разработке линтер будет работать и сообщать вам о нарушениях. - -instructions: | - Выведите на экран результат следующего вычисления: «разность между суммой пяти и двух и произведением трёх и семи». Сравните получившийся результат с решением учителя с точки зрения оформления кода. - -tips: [] diff --git a/modules/20-arithmetics/70-codestyle/ru/data.yml b/modules/20-arithmetics/70-codestyle/ru/data.yml index 9aff761..ff914d7 100644 --- a/modules/20-arithmetics/70-codestyle/ru/data.yml +++ b/modules/20-arithmetics/70-codestyle/ru/data.yml @@ -1,2 +1,3 @@ +--- name: Оформление кода tips: [] diff --git a/modules/30-variables/10-definition/description.ru.yml b/modules/30-variables/10-definition/description.ru.yml deleted file mode 100644 index 773c3c0..0000000 --- a/modules/30-variables/10-definition/description.ru.yml +++ /dev/null @@ -1,115 +0,0 @@ ---- - -name: Что такое переменная? -theory: | - Представьте, что мы пишем программу, которая складывает два числа и выводит результат на экран два или даже пять раз. Можно решить задачу в лоб и написать следующее: - - ```cpp - int main() { - std::cout << 2 + 3; - std::cout << 2 + 3; - } - ``` - - Но тут возникает проблема, что если результат сложения этих двух чисел нам понадобится где-то в программе или мы захотим поменять значение одного из слагаемых? - - Нам придется найти все места в программе и выполнить необходимую замену. Но можно сделать это более изящно. Достаточно определить три переменные, в которых мы будем хранить значение чисел и результат их сложения. Давайте перепишем код с учетом наших новых знаний: - - ```cpp - int main() { - int num_one = 2; - int num_two = 3; - int result = num_one + num_two; - std::cout << result; - return 0; - } - ``` - - Разберем подробнее что тут происходит. Когда мы определяем переменную, в начале указываем ключевое слово, которое обозначает тип данных. - - В данном случае у нас тип int или integer, то есть целое число. После типа идет имя переменной, оператор присваивания и значение которое будет хранится в переменной. - - Стоит учитывать, С++ - это регистрозависимый язык и переменные int num и int Num - это две разные переменные. Кроме того, в качестве имени переменной нельзя использовать ключевые слова языка C++. - - Можно определить переменную, не присваивая ей значение: - - ```cpp - int num; - ``` - - Но так делать не стоит, потому что, если вы ее забудете проинициализировать, компилятор ей присвоит значение по умолчанию. Какое? Тут уже зависит от компилятора и места определения переменной. - - В примере ниже использовался компилятор clang: - - ```cpp - int num_out; - - int main() { - int num_in; - std::cout << "The num_out = " << num_out << std::endl; - std::cout << "The num_in = " << num_in << std::endl; - } - ``` - - Компилятор присвоил переменной значение 0. - -
-    The num_out = 0
-    The num_in = 0
-  
- - Хорошей практикой считается определять переменную ближе к тому месту в коде где она будет использоваться и инициализировать сразу нейтральным значением. - - Например, если это счетчик цикла, то 0, если в переменную будет собираться строка, то пустой строкой. - - ```cpp - int main() { - string acc = ""; - acc += "Hello, "; - acc += "World"; - std::cout << acc; - } - ``` - -
-    Hello, World!
-  
- - C++ поддерживает три основных способа инициализации переменных. Во-первых, мы можем выполнить копирующую инициализацию, используя знак равенства: - - ```cpp - int age = 18; // копирующая инициализация значения 18 в переменную age - ``` - - Подобно копирующему присваиванию, этот код копирует значение с правой стороны знака равно в переменную, создаваемую с левой стороны. Во-вторых, мы можем выполнить прямую инициализацию с помощью скобок: - - ```cpp - int age(18); // прямая инициализация значения 18 в переменную age - ``` - - Для простых типов данных копирующая и прямая инициализации, по сути, одинаковы. Различия между копирующей инициализацией и прямой инициализацией мы разберем далее в курсе. - - И третий тип инициализации - списковая. Списковая инициализация - это более унифицированный механизм инициализации, подходит как для простых типов, так и для составных, которые мы будем рассматривать дальше по курсу. - - Инициализация списком бывает двух форм: - - ```cpp - int width { 5 }; // прямая инициализация списком значения 5 в переменную width (предпочтительно) - int height = { 6 }; - ``` - - Эти две формы функционируют почти одинаково, но обычно предпочтительнее прямая форма. - -instructions: | - Внутри функции `main` определите переменную типа `int`, присвойте ей значение 42 и выведите на экран. Не забудьте сделать перевод строки. - -tips: - - | - [Именование в программировании](https://ru.hexlet.io/blog/posts/naming-in-programming) - [Ключевые слова в С++](https://learn.microsoft.com/ru-ru/cpp/cpp/keywords-cpp?view=msvc-170) - -definitions: - - name: Переменная - description: способ сохранить информацию и дать ей имя для последующего использования в коде. - - name: Ключевые слова - description: зарезервированные слова, которые имеют особое значение для компилятора. Например, `int` или `float` — ключевое слово для объявления переменных. diff --git a/modules/30-variables/10-definition/ru/data.yml b/modules/30-variables/10-definition/ru/data.yml index 3ed3e5e..42c5ac7 100644 --- a/modules/30-variables/10-definition/ru/data.yml +++ b/modules/30-variables/10-definition/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Что такое переменная? tips: - > diff --git a/modules/30-variables/20-change/description.ru.yml b/modules/30-variables/20-change/description.ru.yml deleted file mode 100644 index bdceabc..0000000 --- a/modules/30-variables/20-change/description.ru.yml +++ /dev/null @@ -1,43 +0,0 @@ ---- -name: Изменение переменной - -theory: | - Само слово «переменная», говорит о том, что ее можно менять. И действительно, с течением времени внутри программы, значения переменных могут изменяться. - - ```cpp - int num { 1 }; - std::cout << num << std::endl; - // тип данных указывать не надо, так как переменная была определена выше - num = 2; - std::cout << num << std::endl; - ``` - -
-    1
-    2
-  
- - C++ статически типизированный язык, это значит что тип переменной задается при определении и больше не меняется. - - Прежде всего это связанно с тем, что данные разных типов, по-разному хранятся в памяти компьютера. - - В примере выше, при создании переменной, мы присвоили ей число. Компилятор запоминает тип и проверяет все последующие изменения переменной. Если попробовать этой же переменной присвоить строку, то мы получим следующую ошибку: - - ```cpp - num { "Hello" }; - // error: assigning to 'int' from incompatible type - ``` - - Интересно то, что компилятор делает такую проверку без запуска кода на выполнение, именно поэтому такой вид типизации называют статическим (статика – без запуска). В динамических языках, таких как Javascript, Ruby, PHP или Python, подобное поведение не является ошибкой, переменная может легко изменить свой тип в процессе работы. - -instructions: | - - В коде определена переменная со значением `10`. Переопределите значение этой переменной и присвойте ей значение на единицу больше. Не забудьте сделать перевод строки. - -tips: - - | - Если в редакторе есть запись `// BEGIN` и `// END`, то код нужно писать между этими строчками. - -definitions: - - name: Переменная - description: Способ сохранить информацию и дать ей имя для последующего использования в коде. diff --git a/modules/30-variables/20-change/ru/data.yml b/modules/30-variables/20-change/ru/data.yml index cd21591..28ea8c9 100644 --- a/modules/30-variables/20-change/ru/data.yml +++ b/modules/30-variables/20-change/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Изменение переменной tips: - > diff --git a/modules/30-variables/30-variables-naming/description.ru.yml b/modules/30-variables/30-variables-naming/description.ru.yml deleted file mode 100644 index 2b68505..0000000 --- a/modules/30-variables/30-variables-naming/description.ru.yml +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: Выбор имени переменной -theory: | - Представим себе, что мы пишем программу которая принимает из консоли возраст пользователя и выводит его на экран: - - ```cpp - #include - - int main() { - int user_age { 0 }; - std::cout << "Enter your age: " << std::endl; - std::cin >> user_age; // объект cin отвечает за стандартный ввод - std::cout << user_age << std::endl; - } - ``` - - Давайте изменим название переменной `user_age`: - - ```cpp - int main() { - int x { 0 }; - std::cout << "Enter your age: " << std::endl; - std::cin >> x; // объект cin отвечает за стандартный ввод - std::cout << x << std::endl; - } - ``` - - Она по прежнему работает, но в ней изменилось имя переменной на `x`. Компьютеру без разницы, как мы называем переменные, это бездушная машина, но вот программистам — нет. Мы гораздо чаще читаем код, чем пишем. Причём не свой, а написанный другими людьми. От качества и понятности имён переменных зависит половина успеха в анализе кода. - - Лучше посидеть и придумать название, которое описывает суть, смысл переменной, чем назвать её как попало, а в будущем переделывать. Постарайтесь давать им такие имена, чтобы они были максимально понятны без контекста, без изучения окружающего кода. - - Существует общепринятое правило: не используйте транслит для имён, только английский язык. Если вы испытываете сложности с английским, то пользуйтесь переводчиком. Со временем, копаясь в чужом коде, вы сформируете правильные понятия для именования. - - Среди разработчиков есть шутка: «самое сложное в программировании — названия переменных и инвалидация кеша». Придумывать названия и правда сложно. Как бы вы назвали переменную, в которой хранится _количество неоплаченных заказов от клиентов, имеющих задолженность в предыдущем квартале?_ - - Самопроверка. Придумайте название для переменной, в которой будет храниться _«количество пользователей не совершавших покупки»_. Запишите его в блокноте или отправьте себе на почту. Не указывайте там ничего, кроме названия переменной. А через несколько уроков мы вернёмся к этой теме ;-) - -instructions: | - Создайте переменную, описывающую количество лайков, и присвойте ей значение *2*. Распечатайте содержимое переменной с переносом строки. Затем сравните свое имя с именем, которое используется в учительском решении. - -tips: - - | - [Именование в программировании](https://ru.hexlet.io/blog/posts/naming-in-programming) - - | - [Ошибки в именовании переменных](https://ru.hexlet.io/blog/posts/naming-errors-1) diff --git a/modules/30-variables/30-variables-naming/ru/data.yml b/modules/30-variables/30-variables-naming/ru/data.yml index e695fb2..ee341b6 100644 --- a/modules/30-variables/30-variables-naming/ru/data.yml +++ b/modules/30-variables/30-variables-naming/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Выбор имени переменной tips: - > diff --git a/modules/30-variables/40-errors/description.ru.yml b/modules/30-variables/40-errors/description.ru.yml deleted file mode 100644 index 354224b..0000000 --- a/modules/30-variables/40-errors/description.ru.yml +++ /dev/null @@ -1,67 +0,0 @@ ---- -name: Ошибки при работе с переменными - -theory: | - Порядок следования инструкций в коде с переменными играет огромное значение. Переменная должна быть определена до того, как будет использована. Ниже пример ошибки, которую очень часто допускают новички: - - ```cpp - std::cout << user_age; - int user_age { 18 }; - ``` - - Запуск программы выше завершается с ошибкой: - - ``` - error: use of undeclared identifier 'user_age' - ``` - - Ошибка *error: use of undeclared identifier 'X'* означает, что в коде используется переменная, которая не определена. Причем в самой ошибке прямо говорят какая: `user_age`. Кроме неправильного порядка определения, в C++ встречаются банальные опечатки, причем как при использовании переменной, так и при ее объявлении. - - Еще одна распространенная ошибка — попытаться объявить уже объявленную переменную: - - ```cpp - int user_age { 18 }; - int user_age { 18 }; - ``` - - Так делать нельзя. Придётся создать новую переменную. - - Количество подобных ошибок уменьшается за счет использования правильно настроенного редактора. Такой редактор подсвечивает имена, которые используются без объявления, и предупреждает о возможных проблемах. - - Иногда бывает ситуация когда одна переменная определена глобально, а другая локально и обе они имеют одинаковые имена: - - ```cpp - int user_age { 20 }; // глобальная переменная - - int main() { - int user_age { 18 }; // локальная переменная - std::cout << user_age; - } - ``` - В консоли будет вывод локальной `user_age`: - -
-    18
-  
- - Область видимости глобальной переменной весь файл, но мы не можем обратиться к ней внутри функции, так как ее перекрывает локальная переменная. Такие ошибки трудно уловимы и конечно переменным лучше придумать разные имена. - - На самом деле мы можем обратиться к глобальной переменной используя оператор `::`. Мы уже работали с ним, обращаясь к пространству имён `std::cout`. - - ```cpp - int user_age { 20 }; - - int main() { - int user_age { 18 }; - std::cout << ::user_age; - } - ``` - -
-    20
-  
- - Если перед оператором `::`, это значит мы обращаемся к глобальному пространству имен. С пространством имен мы познакомимся дальше в курсе. - -instructions: | - Найдите в программе необъявленную переменную и объявите ее, присвоив ей значение `27`; diff --git a/modules/30-variables/40-errors/ru/data.yml b/modules/30-variables/40-errors/ru/data.yml index 52155dd..5ef08c7 100644 --- a/modules/30-variables/40-errors/ru/data.yml +++ b/modules/30-variables/40-errors/ru/data.yml @@ -1 +1,2 @@ +--- name: Ошибки при работе с переменными diff --git a/modules/30-variables/50-variables-expressions/description.ru.yml b/modules/30-variables/50-variables-expressions/description.ru.yml deleted file mode 100644 index 0d28c0c..0000000 --- a/modules/30-variables/50-variables-expressions/description.ru.yml +++ /dev/null @@ -1,75 +0,0 @@ ---- -name: Выражения в определениях -theory: | - Переменные полезны не только для хранения и переиспользования информации, но и для упрощения сложных вычислений. Давайте рассмотрим пример: нужно перевести евро в рубли через доллары. Подобные конвертации через промежуточную валюту часто делают банки при покупках за рубежом. - - Для начала переведем 50 евро в доллары. Допустим, что один евро — 1.25 доллара: - - ```cpp - auto dollars_count { 50 * 1.25 }; - std::cout << dollars_count; - ``` - - В предыдущем уроке мы записывали в переменную конкретное значение. А здесь `auto dollars_count { 50 * 1.25 };` справа от знака равно находится **выражение**. Программа вычислит результат — *62.5* — и запишет его в переменную. С точки зрения программы не важно, что написано: *62.5* или *50 * 1.25*, эти оба варианта — выражения, которые надо вычислить. И они вычисляются в одно и тоже значение — *62.5*. - - Вы могли заметить, что появилось новое ключевое слово `auto`. Поскольку мы еще не изучали типы данных в С++, мы указали компилятору, что бы он сам определил тип данных у переменной `dollars_count`. - - Любая строка — выражение. Когда программа видит выражение, она вычисляет его и **возвращает** результат. Вот несколько примеров выражений, а в комментариях справа от каждого выражения — итоговое значение: - - ```cpp - 62.5 // 62.5 - 50 * 1.25 // 62.5 - 120 / 10 * 2 // 24 - - "Hexlet" // "Hexlet" - ``` - - Правила построения кода таковы, что в тех местах, где ожидается выражение, можно поставить любое вычисление (не только математическое, но и, например, строковое — как конкатенация), и программа останется работоспособной. По этой причине невозможно описать и показать все случаи использования всех операций. Программы состоят из множества комбинаций выражений, и понимание этой концепции — один из ключевых шагов на вашем пути. - - Вернемся к нашей валютной программе. Запишем стоимость доллара в рублях, как отдельную переменную. Вычислим цену 50 евро в долларах, умножив их на 1.25. Допустим, что 1 доллар — 60 рублей: - - ```cpp - int main() { - auto rubles_per_dollar { 60 }; - auto dollars_count { 50 * 1.25 }; // 62.5 - auto rubles_count { dollars_count * rubles_per_dollar }; // 3750 - - std::cout << rubles_count; // => 3750 - return 0; - } - ``` - - А теперь давайте добавим к выводу текст: - - ```cpp - int main() { - auto rubles_per_dollar { 60 }; - auto dollars_count { 50 * 1.25 }; // 62.5 - auto rubles_count { dollars_count * rubles_per_dollar }; // 3750 - - std::cout << "The price is " << rubles_count << " rubles" << std::endl; // => The price is 3750 rubles - } - ``` - - Любая переменная может быть частью любого выражения. В момент вычисления вместо имени переменной подставляется её значение. - - Значение `dollars_count` вычисляется до того, как она начнет использоваться в других выражениях. Когда подходит момент использования переменной, C++ «знает» значение, потому что уже вычислил его. - -instructions: | - Напишите программу, которая берет исходное количество евро, записанное в переменную `euros_count`, переводит евро в доллары и выводит на экран. Затем полученное значение переводит в рубли и выводит на новой строчке. Не забудьте в конце вывода добавить перевод строки. - - Пример вывода для 100 евро: - -
-  125.0
-  7500.0
-  
- - Считаем, что: - - - 1 евро = 1.25 долларов - - 1 доллар = 60 рублей - -tips: - - | - Для перевода строчки можно использовать `\n` или `std::endl` между выводом долларов и рублей. diff --git a/modules/30-variables/50-variables-expressions/ru/data.yml b/modules/30-variables/50-variables-expressions/ru/data.yml index 1206dbd..9aad4f5 100644 --- a/modules/30-variables/50-variables-expressions/ru/data.yml +++ b/modules/30-variables/50-variables-expressions/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Выражения в определениях tips: - > diff --git a/modules/30-variables/60-naming-style/description.ru.yml b/modules/30-variables/60-naming-style/description.ru.yml deleted file mode 100644 index 1a1d500..0000000 --- a/modules/30-variables/60-naming-style/description.ru.yml +++ /dev/null @@ -1,36 +0,0 @@ ---- -name: Именование переменных -theory: | - `age` или `number` — пример простого имени, но не все имена так просты. Довольно часто они составные, то есть включают в себя несколько слов. Например, «имя пользователя». В разных языках применяются разные стили кодирования, и имя переменной будет отличаться. - - В именовании переменных можно выделить три основных подхода, которые иногда комбинируют друг с другом. Все эти подходы проявляют себя, когда имя переменной состоит из нескольких слов: - - * kebab-case — составные части переменной разделяются дефисом. Например: `my-super-var`. - * snake_case — для разделения используется подчеркивание. Например: `my_super_var`. - * CamelCase — каждое слово в переменной пишется с заглавной буквы. Например: `MySuperVar`. - * lowerCamelCase — каждое слово в переменной пишется с заглавной буквы, кроме первого. Например: `mySuperVar`. - - В C++ используется смешанный стиль именования: - - * переменные — пишем в стиле snake_case. Например: `current_user`. - * константы — пишем в стиле CamelCase добавляя префикс `k`. Например: `const int kDaysInAWeek = 2`. - * классы — пишем в стиле CamelCase. Например: `MySuperClass`. - * функции — пишем также как и классы в стиле CamelCase. Например: `OpenFile()`. - * файлы - именуются строчными буквами, для разделения можно использовать подчеркивание или дефис. Основные файлы должны иметь расширение .сс, заголовочные .h - - -instructions: | - - Создайте две переменные с именами «первое число» и «второе число» на английском языке, используя snake_case. Запишите в первую переменную число `11`, во вторую — `-100`. Выведите на экран произведение чисел, записанных в получившихся переменных. - - Код будет работать с любыми названиями, а наша система всегда проверяет только результат на экране, поэтому выполнение этого задания — под вашу ответственность. - -tips: - - | - [Google C++ style guide](https://google.github.io/styleguide/cppguide.html) - - | - [Заголовочные файлы](https://learn.microsoft.com/ru-ru/cpp/cpp/header-files-cpp?view=msvc-170) - -definitions: - - name: Стандарт кодирования - description: Набор синтаксических и стилистических правил написания кода. diff --git a/modules/30-variables/60-naming-style/ru/data.yml b/modules/30-variables/60-naming-style/ru/data.yml index eed7998..872dc08 100644 --- a/modules/30-variables/60-naming-style/ru/data.yml +++ b/modules/30-variables/60-naming-style/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Именование переменных tips: - | diff --git a/modules/30-variables/70-magic-numbers/description.ru.yml b/modules/30-variables/70-magic-numbers/description.ru.yml deleted file mode 100644 index f6fb524..0000000 --- a/modules/30-variables/70-magic-numbers/description.ru.yml +++ /dev/null @@ -1,66 +0,0 @@ ---- -name: Магические числа -theory: | - Вспомним один из прошлых уроков: - - ```cpp - // Перевод евро в рубли через доллары - int main() { - int euros { 1000 }; - auto dollars { euros * 1.25 }; // 1250 - auto rubles { dollars * 60 }; // 75000 - - std::cout << rubles << std::endl; // => 75000 - } - ``` - - С точки зрения профессиональной разработки, такой код «пахнет». Так описывают код, который не соответствует так называемым лучшим практикам (best practices). И причина здесь вот в чем: уже сейчас, глядя на числа *60* и *1.25*, вы скорее всего задаетесь вопросом: «что это за числа?». А представьте, что будет через месяц! А как его поймет новый программист, не видевший код ранее? В нашем примере контекст восстанавливается благодаря грамотному именованию, но в реальной жизни код значительно сложнее, и поэтому догадаться до смысла чисел зачастую невозможно. - - Этот «запах» называют магические числа (magic numbers). Числа, происхождение которых невозможно понять без глубокого знания происходящего внутри данного участка кода. - - Выход из ситуации прост: достаточно создать переменные с правильными именами, как все встанет на свои места. - - ```cpp - int main() { - auto dollars_in_euro { 1.25 }; - int rubles_in_dollar { 60 }; - - int euros { 1000 }; - auto dollars { euros * dollars_in_euro }; // 1250 - auto rubles { dollars * rubles_in_dollar }; // 75000 - - std::cout << rubles << std::endl; // => 75000 - } - ``` - - Обратите внимание на следующие детали: - - * Именование *snake_case* - * Две новые переменные отделены от последующих вычислений пустой строчкой. Эти переменные имеют смысл и без вычислений, поэтому такое отделение уместно, оно повышает читаемость. - * Получился хорошо именованный и структурированный код, но он длиннее прошлой версии. Так часто бывает, и это нормально, потому что код должен быть читабельным. - -instructions: | - Вы столкнулись с таким кодом, который выводит на экран среднесуточную температуру в Фаренгейтах: - - ```cpp - std::cout << "Average daily temperature: " << 588 / 24; - ``` - - Как видите, это магические числа: непонятно, что такое _588_ и что такое _24_. - - Избавьтесь от магических чисел, создав новые переменные, а затем выведите текст на экран. - - Получится так: -
-  Average daily temperature: 24
-  
- - Названия переменных должны передавать смысл чисел, но должны при этом оставаться достаточно короткими и ёмкими для комфортного чтения. - - Помните: код будет работать с любыми названиями, а наша система всегда проверяет только результат на экране, поэтому выполнение этого задания — под вашу ответственность. - - В С++ при делении если оба оператора являются целыми числами, то результат будет равен целой доли частного. Незабудьте в конце вывода добавить перевод строки. - -tips: - - | - [Магические числа](https://ru.hexlet.io/blog/posts/magic-numbers) diff --git a/modules/30-variables/70-magic-numbers/ru/data.yml b/modules/30-variables/70-magic-numbers/ru/data.yml index 1d2ce32..f04b759 100644 --- a/modules/30-variables/70-magic-numbers/ru/data.yml +++ b/modules/30-variables/70-magic-numbers/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Магические числа tips: - | diff --git a/modules/40-data-types/10-integer-types/description.ru.yml b/modules/40-data-types/10-integer-types/description.ru.yml deleted file mode 100644 index 22dc210..0000000 --- a/modules/40-data-types/10-integer-types/description.ru.yml +++ /dev/null @@ -1,99 +0,0 @@ ---- -name: Целочисленные типы -theory: | - Целые числа представляют собой бесконечное множество, и в конечном объеме памяти компьютера нельзя представить их все. Таким образом язык может представить только подмножество целых чисел. Отсюда следует определение типа, тип данных - это возможное множество значений. - - Разнообразные целочисленные типы в С++ отличаются друг от друга объемом памяти, выделяемым для хранения целого значения. Чем больше объем памяти, тем шире диапазон предоставляемых целочисленных значений. - - В С++ базовыми целочисленными типами, в порядке увелечения ширины, являются: `char`, `short`, `int`, `long` и `long long`. Каждый из этих типов имеет версии со знаком и без знака. Таким образом, на выбор предоставляется десять целочисленных типов. Давайте познакомимся с ними поближе: - - * `short` - имеет ширину не менее 16 бит и хранит диапазон чисел от - 32768 до 32767 - * `int` - имеет гарантированную ширину, как минимум такую же как `short` и диапазон чисел от -2 147 483 648 до 2 147 483 647 - * `long` - имеет ширину не менее 32 бит и хранит диапазон чисел от -2 147 483 648 до 2 147 483 647 - * `long long` - имеет ширину не менее 64 бит и хранит диапазон чисел от -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807 - - Посмотреть ширину типа в вашей системе можно с помощью оператора `sizeof()`, а диапазоны с помощью символических констант, подключив заголовочный файл стандартной библиотеки `climits`. - - ```cpp - #include - #include - - int main() { - std::cout << "Integer range: from" - << INT_MIN << " to " << INT_MAX << std::endl; - std::cout << "Int type: " << sizeof(int) << " byte" << std::endl; - } - ``` - -
-    Integer range: from-2147483648 to 2147483647
-    Int type: 4 byte
-  
- - - Стоит сказать, что минимальную ширину типа гарантирует язык, а максимальная зависит от архитектуры процессора. - - Каждый из вышеприведённых типов может быть беззнаковым, то есть хранить только неотрицательные числа. За счет этого можно увеличить значение, которое может хранить переменная. Например, если `short` представляет диапазон от -32 768 до 32 767, то `unsigned short` от 0 до 65 532: - - ```cpp - #include - #include - - int main() { - std::cout << "Int max: " - << UINT_MAX << std::endl; - std::cout << "Int type: " << sizeof(unsigned int) << " byte" << std::endl; - } - ``` - -
-    Int max: 4294967295
-    Int type: 4 byte
-  
- - Видно, что верхняя граница увеличилась в два раза, нижняя будет смещена к нулю, но на количестве выделенной памяти это не сказалось. Такое поведение связано с тем, что для хранения отрицательных чисел используется дополнительный бит. - - Что будет если в беззнаковую переменную сохранить отрицательное число или выйти за пределы диапазона знаковой переменной: - - ```cpp - #include - #include - - #define ZERO 0 // определим символьную константу со значением 0 - - int main() { - int int_number { INT_MAX }; // 2147483647 - unsigned u_int_number { ZERO }; // 0 - - std::cout << "new value int_number: " - << int_number + 1 << std::endl; - std::cout << "new value u_int_number: " - << u_int_number - 1 << std::endl; - } - ``` - -
-    new value int_number: -2147483648
-    new value u_int_number: 4294967295
-  
- - Произошла потеря значимости, то есть в обоих случаях мы получили числа другой границы диапазона и компилятор на это не указал. За этим надо следить особенно если вы работаете с большими числами. - - Из всего вышесказанного возникает вопрос: Зачем такой богатый набор типов данных? Допустим, что вы пишете программу для контроллера микроволновой печи и вам известно, что данные которыми будет оперировать программа - это положительные числа, значение которых не превышает 32 000. Естественно ваш выбор падет на `unsigned short` и если у вас большой массив данных, можно существенно сэкономить память. - - В остальных случаях, тип `int` имеет наиболее естественный размер целого числа для целевого компьютера. Под естественным размером подразумевается целочисленная форма, которую компьютер может обработать наиболее эффективным образом. - -instructions: | - Допишите консольную утилиту, которая принимает в качестве аргумента количество секунд и преобразует их в количество дней. Выведите результат на экран. Этап получения аргумента и преобразование его в число уже написан, вам надо дописать логику преобразования секунд в дни. - - Пример вывода: - -
-    86400 seconds = 1 days
-  
- -tips: - - | - [Дополнительный код](https://ru.wikipedia.org/wiki/%D0%94%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%B4) - - | - [](https://learn.microsoft.com/ru-ru/cpp/standard-library/climits?view=msvc-170) diff --git a/modules/40-data-types/10-integer-types/ru/data.yml b/modules/40-data-types/10-integer-types/ru/data.yml index 138289e..54c90ba 100644 --- a/modules/40-data-types/10-integer-types/ru/data.yml +++ b/modules/40-data-types/10-integer-types/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Целочисленные типы tips: - > diff --git a/modules/40-data-types/20-floating-type/description.ru.yml b/modules/40-data-types/20-floating-type/description.ru.yml deleted file mode 100644 index f209980..0000000 --- a/modules/40-data-types/20-floating-type/description.ru.yml +++ /dev/null @@ -1,121 +0,0 @@ ---- -name: Типы данных с плавающей точкой -theory: | - Целочисленные типы отлично подходят для подсчета целых чисел, но иногда нам нужно хранить очень большие числа или числа с дробной частью. Переменная типа с плавающей точкой – это переменная, которая может содержать действительное число, например 4320,0, -3,33 или 0,01226. Название **плавающая точка** указывает на то, что десятичная точка может плавать, то есть она может поддерживать переменное количество цифр до и после себя. - - Существует три разных типа данных с плавающей точкой: `float`, `double` и `long double`. Как и в случае с целыми числами, C++ не определяет фактические размеры этих типов, но гарантирует минимальные размеры: - - * `float` – должен иметь как минимум 32-бита и гарантировать минимум 6 значащих цифр - * `double` – 48-бит и быть не меньше float. Гарантирует 15 значащих цифр - * `long double` – как минимум 80 битов и 18-значащих цифр - - Типы данных с плавающей точкой всегда идут со знаком, то есть могут содержать положительные и отрицательные значения. - - Ниже представлены примеры определения переменных с плавающей точкой: - - ```cpp - float f_num; - double d_num; - long double ld_num; - ``` - - Вы могли заметить, что в имени переменной используется префикс - это делать не обязательно, но это помогает понять переменная кого типа сейчас используется. - - При инициализации переменных такого типа всегда включайте хотя бы один знак после десятичной точки, даже если это ноль. Так компилятор будет понимать, что переменная принадлежит к вещественному типу. - - ```cpp - double d_num { 5.0 }; - float f_num { 5.0f }; - ``` - - По умолчанию литералы с плавающей точкой имеют тип `double`. Суффикс `f` используется для обозначения литерала типа `float`. - - Вы могли заметить, что в коде выше инициализация производится не через оператор присваивания `=`, а с помощью нотации `{ 5.0 }`. Это списковая инициализация, она пришла в язык с 11 стандарта и она имеет одно преимущество: - - ```cpp - int num; - num = 3.14; - std::cout << num; // => 3 - ``` - - В коде выше мы пытаемся сохранить в целочисленную переменную вещественное число. Такая программа скомпилируется и будет работать, но у числа мы потеряем всю вещественную часть. - - Если применить списковую инициализацию, получим ошибку компиляции: - - ```cpp - int num { 3.14 }; // ошибка - int count; - count = { 3.14 }; // ошибка - ``` - - Рассмотрим такую программу: - - ```cpp - int main() { - float f_num = { 10.0 / 3.0 }; - double d_num = { 10.0 / 3.0 }; - std::cout << f_num << std::endl; - std::cout << d_num << std::endl; - } - ``` - -
-    3.33333
-    3.33333
-  
- - Обратите внимание, что каждое из напечатанных значений имеет только 6 значащих цифр. Это стандартное поведение объекта `cout`, но мы можем переопределить это поведение и повысить точность: - - ```cpp - #include - #include // для увеличения точности - - int main() { - std::cout << std::setprecision(16); // вывод с точностью до 16 цифр - - float f_num = { 10.0 / 3.0 }; - double d_num = { 10.0 / 3.0 }; - std::cout << f_num << std::endl; - std::cout << d_num << std::endl; - } - ``` - -
-    3.333333253860474
-    3.333333333333333
-  
- - Видно, что число `float` определено не точно и содержит ошибки, поскольку тип `float` гарантирует нам только шесть значащих цифр. - - Существует две особые категории чисел с плавающей точкой. Первая – `Inf`, которая представляет бесконечность. `Inf` может быть положительной или отрицательной. Вторая – `NaN`, что означает Not a Number - не число. Существует несколько различных типов `NaN`. `NaN` и `Inf` доступны только в том случае, если компилятор для чисел с плавающей точкой использует определенный формат. Если используется другой формат, следующий код приводит к неопределенному поведению. - - ```cpp - #include - - int main() { - double zero { 0.0 }; - double pos_inf { 5.0 / zero }; // положительная бесконечность - std::cout << pos_inf << std::endl; - - double neg_inf { -5.0 / zero }; // отрицательная бесконечность - std::cout << neg_inf << std::endl; - - double nan { zero / zero }; // не число (математически неверно) - std::cout << nan << std::endl; - - return 0; - } - ``` - -
-    inf
-    -inf
-    nan
-  
- -instructions: | - Напишите программу, которая рассчитывает сколько километров проедет автомобиль на полном баке. Пусть объем бака будет 43 литра, а расход 8.8 л на 10 км. Результат выведите на экран. - -tips: - - | - [NaN](https://learn.microsoft.com/ru-ru/cpp/c-runtime-library/reference/nan-nanf-nanl?view=msvc-170) diff --git a/modules/40-data-types/20-floating-type/ru/data.yml b/modules/40-data-types/20-floating-type/ru/data.yml index 658b022..ceff378 100644 --- a/modules/40-data-types/20-floating-type/ru/data.yml +++ b/modules/40-data-types/20-floating-type/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Типы данных с плавающей точкой tips: - > diff --git a/modules/40-data-types/30-logic-type/description.ru.yml b/modules/40-data-types/30-logic-type/description.ru.yml deleted file mode 100644 index bfde2d1..0000000 --- a/modules/40-data-types/30-logic-type/description.ru.yml +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: Логический тип -theory: | - Начиная с 11 стандарта в С++ был добавлен новый для этого языка тип по имени `bool`. Он назван в честь английского математика Джорджа Буля, разработавшего математическое представление законов логики. - - Булевская переменная может принимать два значения: `true` (истина) или `false` (ложь). Для хранения булевской переменной выделяется один байт: - - ```cpp - bool is_even; - std::cout sizeof(is_even); // => 1 - ``` - - В ранних версиях языка С++ булевский тип отсутствовал. В место этого С++ интерпретировал ненулевые значения как `true`, а нулевые как `false`. - - Литералы `true` и `false` могут быть преобразованы в тип `int`, причем истина будет преобразована в единицу, а ложь - в ноль: - - ```cpp - int is_even { true }; - int promise { false }; - std::cout << is_even; // => 1 - std::cout << promise; // => 0 - ``` - - Кроме того, любое числовое значение может быть преобразовано неявно в значение `bool`: - - ```cpp - bool is_even { 0 }; // false - bool promise { 1 }; // true - false == 0; // true - ``` - -instructions: | - Допишите программу, которая принимает в качестве аргумента командной строки число и определяет его четность. Результат сохраните в переменной типа `bool` и выведите на экран. Не забудьте сделать перевод строки. - -tips: - - | - [bool](https://learn.microsoft.com/ru-ru/cpp/cpp/bool-cpp?view=msvc-170) diff --git a/modules/40-data-types/30-logic-type/ru/data.yml b/modules/40-data-types/30-logic-type/ru/data.yml index d418c89..6467a1d 100644 --- a/modules/40-data-types/30-logic-type/ru/data.yml +++ b/modules/40-data-types/30-logic-type/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Логический тип tips: - | diff --git a/modules/40-data-types/40-char-type/description.ru.yml b/modules/40-data-types/40-char-type/description.ru.yml deleted file mode 100644 index 90f6efc..0000000 --- a/modules/40-data-types/40-char-type/description.ru.yml +++ /dev/null @@ -1,65 +0,0 @@ ---- -name: Тип сhar -theory: | - В этом уроке мы рассмотрим последний целочисленный тип: `char`. - - `сhar` предназначен для хранения символов, таких как буквы и цифры. Но почему же `char` это целочисленный тип? Все дело в том, что хранение чисел в памяти компьютера не представляет сложности, тогда как хранение букв связанно с рядом проблем. Поэтому в языках программирования принят простой подход: хранить символы в памяти компьютеров в виде числовых кодов. - - Таким образом тип `char` является еще одним целочисленным типом. - - ```cpp - int main() { - char symbol { 'M' }; - int number { symbol }; - std::cout << symbol << std::endl; // => M - std::cout << number << std::endl; // => 77 - return 0; - } - ``` - - Интересный момент состоит в том, что на самом деле и в переменной `number` и в переменной `symbol` хранится значение _77_, а когда дело доходит до вывода объект `cout` по-разному интерпретирует эти переменные. - - `char` по умолчанию может быть как беззнаковым так и знаковым типом. Тут все зависит от компилятора. Если для нас крайне важно, что бы тип `char` обладал определенным поведением, надо указать это явно: - - ```cpp - unsigned char symbol // беззнаковый, диапазон от 0 до 255 - signed char symbol // знаковый, диапазон от -128 до 127 - ``` - - Под переменную типа `char` выделяется один байт, для работы с символами в кодировке ASCII этого вполне достаточно, но для работы с Unicode нет. Если мы попытаемся определить переменную типа `char`, получим ошибку переполнения: - - ```cpp - int main() { - char symbol = 'Ф'; - return 0; - } - ``` - -
-    main.cpp:2:21: error: narrowing conversion of '53412' from 'int' to 'char' [-Wnarrowing]
-    2 |     char symbol = 'Ф';
-      |                   ^~~
-  
- - Для работы с символами которые превышают один байт, есть расширенный тип `wchar_t` - под котрый выделяется два байта памяти, а начиная со стандарта С++ 11 `char16_t` и `char32_t`: - - ```cpp - int main() { - wchar_t symbol = L'Ф'; - char16_t symbol_16 = u'Ю'; - char32_t symbol_32 = U'Д'; - return 0; - } - ``` - Что бы указать принадлежность к тому или иному символьному типу, перед символом ставится префикс. Например, префикс `L` обозначает расширенный строковый литерал. - -instructions: | - Определите внутри функции `main()` переменную типа `char` и сохраните в нее символ `U`. Выведите значение переменной в консоль. - -tips: - - | - [setlocale](https://learn.microsoft.com/ru-ru/cpp/c-runtime-library/reference/setlocale-wsetlocale?view=msvc-170) - - | - [Стандартная библиотека](https://learn.microsoft.com/ru-ru/cpp/build/reference/std-specify-language-standard-version?view=msvc-170) - - | - [ASCII](https://ru.wikipedia.org/wiki/ASCII) diff --git a/modules/40-data-types/40-char-type/ru/data.yml b/modules/40-data-types/40-char-type/ru/data.yml index c26a3b2..56d16e6 100644 --- a/modules/40-data-types/40-char-type/ru/data.yml +++ b/modules/40-data-types/40-char-type/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Тип сhar tips: - > diff --git a/modules/40-data-types/50-type-casting/description.ru.yml b/modules/40-data-types/50-type-casting/description.ru.yml deleted file mode 100644 index 0811a2c..0000000 --- a/modules/40-data-types/50-type-casting/description.ru.yml +++ /dev/null @@ -1,101 +0,0 @@ ---- -name: Преобразование типов -theory: | - Как мы уже поняли разные типы данных по-разному хранятся в памяти компьютера. Например, целочисленное значение 3 может быть сохранено как двоичное 0000 0000 0000 0000 0000 0000 0000 0011, тогда как значение с плавающей запятой 3.0 может быть сохранено как двоичное 0100 0000 0100 0000 0000 0000 0000 0000. - - Так что же происходит, когда мы инициализируем переменную типа `float` целым числом? - - ```cpp - float f_age { 18 }; - ``` - - А произойдет следующее: поскольку компилятор не может просто сохранить целое число в переменную с плавающей точкой, он преобразует это число в эквивалентное, но типа `float`. - - Процесс преобразования значения из одного типа данных в другой тип данных называется преобразованием типа. - - Преобразования типов могут быть неявные - по решению компилятора и явные по решению программиста. - - Неявное преобразование типа выполняется компилятором автоматически, когда требуется один тип данных, но предоставляется другой тип. Подавляющее большинство преобразований типов в C++ являются неявными преобразованиями типов. - - Неявное преобразование может происходить в следующих случаях: - - * если мы делаем арифметическую операцию двух разных типoв. Преобразование идет в тот тип, который шире. - - ```cpp - double d_result { 4.2 / 3 } // 3 будет преобразована в double - ``` - - * если происходит инициализация переменной другого типа. Например, мы сохраним число типа `long` в переменную типа `int`. И тут преобразование уже будет зависеть от способа инициализации переменной. - - ```cpp - double d_num { 3 }; // 3 будет преобразована в double - int num = 3.14; // будет потеряна вещественная часть - int num = { 3.14 }; // ошибка компиляции - ``` - - * Забегая вперед. При использовании небулевого значения в инструкции if - - ```cpp - if (5) { } // 5 будет преобразованно в true - ``` - - Но явное всегда лучше, чем неявное. В C++ существует 5 различных видов приведений типа: приведения в стиле C, статические приведения, константные приведения, динамические приведения и реинтерпретирующие приведения. Последние четыре иногда называют именованными приведениями. - - Здесь мы рассмотрим приведение в стиле С и статическое приведение. - - В стандартном программировании на C приведение типов выполняется с помощью оператора (), при этом имя типа, в который необходимо преобразовать значение, помещается в круглые скобки. Вы всё еще можете увидеть, что они используются в коде, преобразованном из C. - - ```cpp - int main() { - int x { 5 }; - int y { 4 }; - double d_result { (double)x / y }; // x преобразуется в тип double - } - ``` - - В приведенном выше коде мы явно указали компилятору, чтобы он преобразовал `int x` в `double`. - - C++ также позволяет вам использовать приведение в стиле C с синтаксисом, более похожим на вызов функций: - - ```cpp - double d_result { double(x) / y }; - ``` - Это работает идентично предыдущему примеру, но тут преимущество в том, что преобразуемое значение заключено в скобки - это упрощает определение того, что конвертируется. - - Приведение в стиле С не рекомендуется использовать, поскольку под капотом он может выполнять множество различных преобразований в зависимости от контекста и даже убирать константность. - - В C++ появился оператор приведения типов `static_cast`, который можно использовать для преобразования значения одного типа в значение другого типа. Этот способ наиболее безопасен и рекомендован в С++. - - ```cpp - int main() { - int x { 5 }; - int y { 4 }; - double d_result { static_cast(x) / y }; - } - ``` - - В угловых скобках указывается тип, к которому приводим, а в круглые скобки передаем переменную или число, которое приводим. - - -instructions: | - В прошлом модуле мы писали программу, которая переводила евро в доллары, а доллары в рубли. На тот момент мы не знали типов данных с плавающей точкой и использовали автоматический вывод типов с помощью `auto`. Отрефакторите ее, используя новые знания. Используйте явное приведение типов. - - Напишите программу, которая берет исходное количество евро, записанное в переменную `euros_count`, переводит евро в доллары и выводит на экран. Затем полученное значение переводит в рубли и выводит на новой строчке. - - Пример вывода для 100 евро: - -
-  125.0
-  7500.0
-  
- - Считаем, что: - - - 1 евро = 1.65 долларов - - 1 доллар = 60 рублей - -tips: - - | - [static_cast](https://learn.microsoft.com/ru-ru/cpp/cpp/static-cast-operator?view=msvc-170) - - | - [Преобразования типов и безопасность типов](https://learn.microsoft.com/ru-ru/cpp/cpp/type-conversions-and-type-safety-modern-cpp?view=msvc-170) diff --git a/modules/40-data-types/50-type-casting/ru/data.yml b/modules/40-data-types/50-type-casting/ru/data.yml index ca62599..8499e23 100644 --- a/modules/40-data-types/50-type-casting/ru/data.yml +++ b/modules/40-data-types/50-type-casting/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Преобразование типов tips: - > diff --git a/modules/40-data-types/60-type-alias/description.ru.yml b/modules/40-data-types/60-type-alias/description.ru.yml deleted file mode 100644 index f25d1ea..0000000 --- a/modules/40-data-types/60-type-alias/description.ru.yml +++ /dev/null @@ -1,65 +0,0 @@ ---- -name: Алиасы типов typedef и using -theory: | - В этом уроке мы научимся как определять псевдонимы типов в С++ и узнаем для чего это может быть полезным. - - Представьте что мы пишем программу, которая рассчитывает расстояние пройденное автомобилем за какое-то время и мы бы могли написать ее так: - - ```cpp - int main() { - int speed_in_kilometers { 60 }; - int travel_time { 2 }; - int distance { speed_in_kilometers * travel_time }; - } - ``` - - Но возможность определения псевдонимов может сделать наш код более выразительным, синтаксис определения псевдонимов таков `typedef <тип> <псевдоним>`: - - ```cpp - int main() { - typedef int kilometers_per_hour_t; // определяем kilometers_per_hour_t как псевдоним типа int - typedef int hour_t; - kilometers_per_hour_t speed { 60 }; - hour_t travel_time { 2 }; - } - ``` - - Названия типов стали длиннее, но более понятыми. Мы видим, что скорость измеряется в километрах в час, а время в часах. Обратите внимание, что хорошей практикой является у псевдонима определять суффикс `_t`. Это помогает указать, что идентификатор представляет собой тип, а не переменную или функцию, а также помогает предотвратить конфликты имен с другими типами идентификаторов. - - Когда еще нам могут понадобиться псевдонимы? Например, для определения псевдонимов сложных типов. Далее в курсе мы будем иметь дело с такими типами данных, как вектор и пара. Их определение довольно громоздко: - - ```cpp - #include // для std::vector - #include // для std::pair - - int main() { - std::vector > pair_list; - } - ``` - - Ввод `std::vector >` везде, где вам нужно использовать этот тип, может оказаться громоздким. Гораздо проще использовать псевдоним типа: - - ```cpp - typedef std::vector > pair_list_t; - int main() { - pair_list_t pair_list; - } - ``` - - Так же есть альтернативный новый синтаксис определения псевдонимов с помощью ключевого слова `using` который пришел в С++ с 11 стандартом: - - ```cpp - using kilometers_per_hour_t = int; - ``` - - В соответствии со стандартами кодирования предпочтительнее определять псевдоним через `using` чем через `typedef`. Так же `typedef` имеет ограничение, он не работает с шаблонами. - - Так же следует учитывать, что `typedef` и `using` не определяет новый тип. Скорее, он просто создает новый идентификатор (псевдоним) для существующего типа. - - -instructions: | - В этом задании мы вспомним школьный курс физики. Напишите программу, которая рассчитывает максимальную мощность, на которую рассчитана бытовая розетка и выводит ее в консоль. Мощность рассчитывается по формуле: сила тока умноженная на напряжение. Сила тока измеряется в Ампер и равна 16, напряжение в Вольт, мощность в Ватт. Для определения величин используйте псевдонимы. Не забудьте сделать перевод строки. - -tips: - - | - [Псевдонимы](https://learn.microsoft.com/ru-ru/cpp/cpp/aliases-and-typedefs-cpp?view=msvc-170) diff --git a/modules/40-data-types/60-type-alias/ru/data.yml b/modules/40-data-types/60-type-alias/ru/data.yml index 71fa95e..7541cb6 100644 --- a/modules/40-data-types/60-type-alias/ru/data.yml +++ b/modules/40-data-types/60-type-alias/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Алиасы типов typedef и using tips: - > diff --git a/modules/40-data-types/70-const-type/description.ru.yml b/modules/40-data-types/70-const-type/description.ru.yml deleted file mode 100644 index b78fbb9..0000000 --- a/modules/40-data-types/70-const-type/description.ru.yml +++ /dev/null @@ -1,72 +0,0 @@ ---- -name: Квалификаторы const и constexpr -theory: | - В этом уроке мы познакомимся с константами и узнаем какие виды констант бывают в С++. - - До сих пор все переменные, которые мы видели, были непостоянными, то есть их значения можно изменить в любое время. Однако иногда бывает полезно определять переменные со значениями, которые нельзя изменить. Например, есть математическая постоянная число Пи: 3,14. Маловероятно, что в ближайшее время оно изменится. Определение этого значения как константы помогает гарантировать, что оно не будет случайно изменено. - - Чтобы сделать переменную константой, просто поместите ключевое слово `const` до или после типа переменной, например: - - ```cpp - const double kPi { 3.14 }; - int const kMonthInYear { 12 }; - ``` - - Хотя оба варианта считаются валидным, все же стандартами кодирования рекомендуется использовать первый. - - При объявлении константы мы сразу же должны инициализировать ее значением, иначе получим ошибку компиляции. - - ```cpp - const double kGravity; // ошибка компиляции, должна быть инициализирована при определении - ``` - - Обратите внимание, что константные переменные могут быть инициализированы из других переменных: - - ```cpp - int main() { - std::cout << "Enter your age: "; - int age {}; - std::cin >> age; - - const int kUsersAge { age }; - } - ``` - - ## Константы времени выполнения и константы времени компиляции - - На самом деле C++ имеет два разных типа констант. - - Константы времени выполнения – это те, значения инициализации которых могут быть вычислены только во время выполнения, когда ваша программа работает. Такие переменная, как `kUsersAge` в приведенном выше фрагменте, является константой времени выполнения, поскольку компилятор не может определить ее начальное значение во время компиляции. kUsersAge полагается на ввод данных пользователем (который может быть предоставлен только во время выполнения). Однако после инициализации значение этой константы изменить нельзя. - - Константы времени компиляции – это те, чьи значения инициализации могут быть вычислены во время компиляции. - - ```cpp - const double kGravity { 9.8 }; - ``` - - Переменная `kGravity` выше является примером постоянной времени компиляции. Константы времени компиляции позволяют компилятору выполнять оптимизацию, недоступную для констант времени выполнения. Например, всякий раз, когда используется `kGravity`, компилятор может просто заменить идентификатор `kGravity` литералом 9.8 типа `double`. - - Чтобы обеспечить большую конкретность, в C++11 введено ключевое слово `constexpr`, которое гарантирует, что константа должна быть константой времени компиляции: - - ```cpp - int main() { - constexpr double kGravity { 9.8 }; // значение 9,8 может быть определено во время компиляции - constexpr int kSum { 4 + 5 }; // значение 4 + 5 может быть определено во время компиляции - - std::cout << "Enter your age: "; - int age{}; - std::cin >> age; - constexpr int kMyAge { age }; // ошибка компиляции - } - ``` - - В этом уроке мы познакомились с константами в С++ и узнали что константы бывают двух видов: времени выполнения и времени компиляции. - -instructions: | - Допишите программу, которая принимает в качестве аргумента командной строки радиус, находит длину окружности и выводит ее на экран. Длина окружности вычисляется по формуле: С = 2 * pi * r. Число Пи задайте константой. Не забудьте добавить в вывод перевод строки. - -tips: - - | - [const](https://learn.microsoft.com/ru-ru/cpp/cpp/const-cpp?view=msvc-170) - - | - [Константность](https://ru.wikipedia.org/wiki/Const_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)) diff --git a/modules/40-data-types/70-const-type/ru/data.yml b/modules/40-data-types/70-const-type/ru/data.yml index 9384410..ec9bc33 100644 --- a/modules/40-data-types/70-const-type/ru/data.yml +++ b/modules/40-data-types/70-const-type/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Квалификаторы const и constexpr tips: - | diff --git a/modules/40-data-types/80-string-type/description.ru.yml b/modules/40-data-types/80-string-type/description.ru.yml deleted file mode 100644 index 4f4391c..0000000 --- a/modules/40-data-types/80-string-type/description.ru.yml +++ /dev/null @@ -1,78 +0,0 @@ ---- -name: Строки и std::string -theory: | - Вспомним нашу первую программу: - - ```cpp - main() { - std::cout << "Hello, World!"; - } - ``` - - "Hello, world!" — это набор последовательных символов, который называется строкой. В C++ мы используем строки для представления текста, такого как имена, адреса, слова и предложения. - - В этом уроки мы познакомимся со строками в С++. - - ## Особенность строк в С++ - - Строки в С++ не являются стандартным типом. Это составной тип, определенный в стандартной библиотеке, а не в ядре самого языка. Но строки достаточно просты и полезны, поэтому мы познакомимся с ними здесь, а в уроках про составные типы данных изучим их подробнее. - - Чтобы использовать строки, нам нужно подключить заголовочный файл `` из стандартной библиотеки: - - ```cpp - #include - - std::string domain { "code-basics" }; - ``` - - Тип `string` объявляется через пространство имен `std`. А строка в отличие от символа должна быть заключена в двойные кавычки. - - ## Длина строки - - Если мы хотим знать, сколько символов находится в `std::string`, мы можем спросить переменную `std::string` о ее длине. Синтаксис для этого отличается от того, что мы видели раньше: - - ```cpp - #include - #include - - int main() { - std::string domain { "code-basics" }; - std::cout << "Length domain name:" << " " << domain.length() << std::endl; - return 0; - } - ``` - - Вместо того, чтобы запрашивать длину строки как `length(domain)`, мы говорим `domain.length()`. Функция `length()` не является обычной автономной функцией. Это особый тип функции, которая принадлежит `std::string` и называется функцией-членом. - - Позже мы расскажем о функциях-членах и о том, как написать собственные. - - Эта программа создает следующий вывод: - -
-    Length domain name: 11
-  
- - - У функции `length()` есть алиас `size()`, который работает идентично: - - ```cpp - int main() { - std::string domain { "code-basics" }; - std::cout << "Length domain name:" << " " << domain.size() << std::endl; - return 0; - } - ``` - -
-    Length domain name: 11
-  
- - `std::string` сложен и использует многие языковые функции, которые мы еще не рассмотрели. Но нам не нужно разбираться в этих сложностях, чтобы использовать std::string для простых задач, таких как простейший ввод и вывод строк. - - Мы рекомендуем начать экспериментировать со строками уже сейчас, а дополнительные возможности строк мы рассмотрим позже. - -instructions: | - Допишите программу, которая принимает в качестве аргумента командной строки доменное имя, сохраните его в переменную типа `std::string` и выведите на экран количество символов этого имени. Получить аргумент командной строки можно таким образом: `argv[1]` -tips: - - | - [std::string](https://en.cppreference.com/w/cpp/string/basic_string) diff --git a/modules/40-data-types/80-string-type/ru/data.yml b/modules/40-data-types/80-string-type/ru/data.yml index 8ec8d83..32921eb 100644 --- a/modules/40-data-types/80-string-type/ru/data.yml +++ b/modules/40-data-types/80-string-type/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Строки и std::string tips: - | diff --git a/modules/45-ref-and-pointers/10-introduction-pointers/description.ru.yml b/modules/45-ref-and-pointers/10-introduction-pointers/description.ru.yml deleted file mode 100644 index c9609d9..0000000 --- a/modules/45-ref-and-pointers/10-introduction-pointers/description.ru.yml +++ /dev/null @@ -1,83 +0,0 @@ ---- -name: Введение в указатели -theory: | - **Переменная** — это имя части памяти, которая содержит значение. Когда наша программа создает экземпляр переменной, ей автоматически присваивается адрес свободной памяти. Любое значение, которое мы присваиваем переменной, сохраняется в памяти с этим адресом. - - Например: - - ```cpp - int num {}; - ``` - - Когда эта инструкция выполняется процессором, будет выделена часть памяти из ОЗУ. - - В качестве примера предположим, что переменной `num` присвоена ячейка памяти 140. Когда программа видит переменную `num` в выражении или инструкции, она знает, что она должна искать значение в ячейке памяти 140. - - Нам не нужно беспокоиться о том, какой адрес памяти назначен. Мы просто ссылаемся на переменную по ее заданному идентификатору, и компилятор переводит это имя в соответствующий адрес памяти. - - Однако у этого подхода есть некоторые ограничения, которые мы обсудим в этом и будущих уроках. - - ## Оператор адреса `&` - - Оператор адреса `&` позволяет нам увидеть, какой адрес памяти назначен переменной. Это довольно просто: - - ```cpp - #include - - int main() { - int num { 5 }; - std::cout << num << std::endl; // выводим значение переменной num - std::cout << &num << std::endl; // выводим адрес памяти переменной num - - return 0; - } - ``` - - Эта программа создает следующий вывод: - -
-    5
-    0x7ffff5af31bc
-  
- - На вашей машине адрес может быть другой, так как адресное пространство памяти, выделенное программе, может отличаться. - - Хотя оператор адреса выглядит как оператор побитового И, их можно различить, поскольку оператор адреса является унарным, тогда как оператор побитового И является бинарным. - - Теперь имея адрес переменной мы можем с помощью оператора косвенного обращения, получить значение переменной. - - ## Оператор косвенного обращения `*` - - **Оператор косвенного обращения** `*` также называют оператором разыменования. Он позволяет получить доступ к значению по определенному адресу: - - ```cpp - #include - - int main() { - int num { 5 }; - std::cout << num << std::endl; // выводим значение переменной num - std::cout << &num << std::endl; // выводим адрес памяти переменной num - std::cout << *(&num) << std::endl; // выводим значение по адресу памяти переменной num - // (скобки не требуются, но упрощают чтение) - - return 0; - } - ``` - - Обратим внимание на выражение `*(&num)`. Мы с начала получили адрес переменной через оператор `&` и уже потом через адрес — значение. Эта программа создает следующий вывод: - -
-    5
-    0x7fff9cea58c0
-    5
-  
- - Теперь у нас есть важные инструменты для работы с указателями, к изучению которых мы приступим в следующем уроке. - -instructions: | - Допишите программу, которая берет значение по адресу переменной `domain_name` и выводит на экран. -tips: - - | - [Оператор косвенного обращения](https://learn.microsoft.com/ru-ru/cpp/cpp/indirection-operator-star?view=msvc-170) - - | - [Оператор address-of: &](https://learn.microsoft.com/ru-ru/cpp/cpp/address-of-operator-amp?view=msvc-170) diff --git a/modules/45-ref-and-pointers/10-introduction-pointers/ru/data.yml b/modules/45-ref-and-pointers/10-introduction-pointers/ru/data.yml index 090e2eb..1951d65 100644 --- a/modules/45-ref-and-pointers/10-introduction-pointers/ru/data.yml +++ b/modules/45-ref-and-pointers/10-introduction-pointers/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Введение в указатели tips: - > diff --git a/modules/45-ref-and-pointers/20-pointers/description.ru.yml b/modules/45-ref-and-pointers/20-pointers/description.ru.yml deleted file mode 100644 index 77d835e..0000000 --- a/modules/45-ref-and-pointers/20-pointers/description.ru.yml +++ /dev/null @@ -1,240 +0,0 @@ ---- -name: Указатели -theory: | - Когда в инструменты добавлены оператор адреса и оператор косвенного обращения, можно поговорить об указателях. В этом уроке мы узнаем что такое указатели, как их объявлять и какие существуют базовые операции. - - ## Объявление указателя - - **Указатель** — это переменная, которая в качестве значения хранит адрес памяти. - - Переменные-указатели объявляются так же, как обычные переменные. Только в этом случае ставится звездочка между типом данных и именем переменной. Эта звездочка не является косвенным обращением. Это часть синтаксиса объявления указателя: - - ```cpp - int *i_ptr {}; // указатель на значение типа int - double *d_ptr {}; // указатель на значение типа double - - int* i_ptr2 {}; // тоже допустимый синтаксис - int * iPtr3{}; // тоже допустимый синтаксис (но не делайте так, это похоже на умножение) - ``` - - Синтаксически C++ принимает звездочку рядом с типом данных, рядом с именем переменной или даже в середине. - - При объявлении переменной-указателя нужно ставить звездочку рядом с типом, чтобы его было легче отличить от косвенного обращения. - - Как и обычные переменные, указатели не инициализируются при объявлении. Если они не инициализированы значением, они будут содержать мусор. - - Указатель X (где X – какой-либо тип) — это обычно используемое сокращение для «указателя на X». Поэтому, когда мы говорим «указатель int», мы на самом деле имеем в виду «указатель на значение типа int». - - Хорошей практикой считается инициализировать указатель значением. - - ## Присвоение значения указателю - - Поскольку указатели содержат только адреса, когда мы присваиваем значение указателю, это значение должно быть адресом. Одна из самых распространенных вещей, которые делают с указателями, – это хранение в них адреса другой переменной. - - Чтобы получить адрес переменной, мы используем оператор адреса: - - ```cpp - #include - - int main() { - int num { 5 }; - int* ptr { &num }; // инициализируем ptr адресом переменной num - - std::cout << &num << '\n'; // выводим адрес переменной num - std::cout << ptr << '\n'; // выводим адрес, который хранится в ptr - - return 0; - } - ``` - - Эта программа создает следующий вывод: - -
-    0x7ffc5d336fc8
-    0x7ffc5d336fc8
-  
- - `ptr` содержит адрес значения переменной, поэтому мы говорим, что `ptr` «указывает на» `num`. - - Тип указателя должен соответствовать типу переменной, на которую он указывает: - - ```cpp - int i_value { 5 }; - double d_value { 7.0 }; - - int* i_ptr { &iValue }; // ok - double* d_ptr { &dValue }; // ok - i_ptr = &d_value; // ошибка - ``` - - Тип `double` не может указывать на адрес переменной типа `int`. Следующее также некорректно: - - ```cpp - int* ptr { 5 }; - ``` - Это связано с тем, что указатели могут содержать только адреса, а целочисленный литерал 5 не имеет адреса памяти. Если попробовать сделать это, компилятор сообщит, что он не может преобразовать `int` в указатель `int`. - - Вопрос на засыпку: Можно ли инициализировать указатель, явно указав адрес ячейки памяти? - - ```cpp - double* d_ptr{ 0x0012FF7C }; - ``` - - Ответ - нет, компиляция этого кода завершится с ошибкой! Хотя казалось бы, почему, ведь оператор адреса `&`, так же возвращает адрес? Тут есть отличие - оператор `&` возвращает тоже указатель. - - ## Возвращение указателя оператором адреса - - Оператор адреса (&) не возвращает адрес своего операнда в виде литерала. Вместо этого он возвращает указатель, содержащий адрес операнда, тип которого является производным от аргумента. Например, взятие адреса значения `int` вернет адрес в указателе `int`. - - Мы можем увидеть это в следующем примере: - - ```cpp - #include - #include - - int main() { - int num { 4 }; - std::cout << typeid(&int).name() << std::endl; - - return 0; - } - ``` - - В Visual Studio этот код напечатал: - -
-    int *
-  
- - При компиляции gcc вместо этого выводит "pi" («pointer to int», указатель на int). - - Одной из основных операций является получение значения переменной через указатель - косвенное обращение. - - ## Косвенное обращение через указатели - - У нас есть переменная-указатель, которая указывает на что-то. Значит, другая распространенная вещь, которую мы делаем с ней, — это косвенное обращение через указатель. Это нужно, чтобы получить значение того, на что он указывает. - - Косвенное обращение через указатель вычисляет содержимое адреса, на который он указывает: - - ```cpp - int value { 5 }; - std::cout << &value << std::endl; // выводит адрес value - std::cout << value << std::endl; // выводит содержимое value - - int* ptr { &value }; // ptr указывает на value - std::cout << ptr << std::endl; // выводит адрес, содержащийся в ptr, который равен &value - std::cout << *ptr << std::endl; // косвенное обращение через ptr — получаем значение, на которое указывает ptr - ``` - - Эта программа создает следующий вывод: - -
-    0x7ffcc0b6824c
-    5
-    0x7ffcc0b6824c
-    5
-  
- - Без типа при косвенном обращении через указатель он не знал бы, как интерпретировать содержимое, на которое он указывает. По этой же причине тип указателя и тип переменной, адрес которой ему присваивается, должны совпадать. Если бы это было не так, косвенное обращение через указатель неверно интерпретировало бы биты как другой тип. - - После присваивания значению указателя можно присвоить другое значение: - - ```cpp - int value1{ 5 }; - int value2{ 7 }; - - int* ptr{}; - - ptr = &value1; // ptr указывает на value1 - std::cout << *ptr; // выводит 5 - - ptr = &value2; // ptr теперь указывает на value2 - std::cout << *ptr; // выводит 7 - ``` - - Когда адрес переменной `value` присваивается указателю `ptr`, верно следующее: - - - `ptr` равен `&value` - - `*ptr` обрабатывается так же, как value - - Поскольку `*ptr` обрабатывается так же, как `value`, можно присваивать ему значения, как если бы это была переменная `value`. - - Следующая программа напечатает 7: - - ```cpp - int value { 5 }; - int* ptr { &value }; // ptr указывает на value - - *ptr = 7; // *ptr - это то же, что и value, которому присвоено 7 - std::cout << value << std::endl; // выводит 7 - ``` - - Обратите внимание, через указатель мы можем работать с переменной `value` - получить значение, и даже изменить его. - - Такой мощный механизм имеет свои минусы. - - ## Предупреждение о косвенном обращении через недействительные указатели - - Указатели в C++ по своей сути небезопасны. Неправильное использование указателей — один из лучших способов вывести приложение из строя. - - Во время косвенного обращения через указатель приложение пытается перейти в ячейку памяти, которая хранится в указателе, и получить содержимое памяти. В целях безопасности современные операционные системы используют приложения-песочницы. Они предотвращают неправильное взаимодействие операционной системы с другими приложениями и защитить стабильность самой операционной системы. - - Если приложение пытается получить доступ к области памяти, не выделенной ему операционной системой, операционная система может завершить работу приложения. - - Следующая программа иллюстрирует это и вероятнее всего упадет с ошибкой: - - ```cpp - #include - - // Мы рассмотрим & позже. Пока не беспокойтесь об этом. Мы используем его только для того, - // чтобы заставить компилятор думать, что p имеет значение. - void foo(int*&p) { - // p — ссылка на указатель. Мы рассмотрим ссылки (и ссылки на указатели) позже в этой главе. - // Мы используем ее, чтобы заставить компилятор думать, что p мог быть изменен, - // поэтому он не будет жаловаться на то, что p неинициализирован. - } - - int main() { - int* p; // Создаем неинициализированный указатель (указывающий на мусор) - foo(p); // Обманываем компилятор, заставляя его думать, что мы собираемся присвоить указателю допустимое значение - - std::cout << *p << std::endl; // Косвенное обращение через указатель на мусор - - return 0; - } - ``` - - Для хранения указателей так же как и для обычных приманных выделяется память. - - ## Размер указателей - - Размер указателя зависит от архитектуры, для которой скомпилирован исполняемый файл — 32-битный исполняемый файл использует 32-битные адреса памяти. Следовательно, указатель на 32-битной машине занимает 32 бита (4 байта). С 64-битным исполняемым файлом указатель будет 64-битным (8 байтов). Это независимо от размера объекта, на который он указывает: - - ```cpp - char* ch_ptr {}; // char равен 1 байту - int* i_ptr {}; // int обычно равен 4 байтам - - std::cout << sizeof(ch_ptr) << std::endl; // выводит 4 - std::cout << sizeof(i_ptr) << std::endl; // выводит 4 - ``` - - Размер указателя всегда один и тот же. Это связано с тем, что указатель — это просто адрес памяти, а количество битов, необходимых для доступа к адресу памяти на данной машине, всегда постоянно. - - Что хорошего в указателях: - - - Массивы реализованы с помощью указателей. Указатели могут использоваться для итерации по массиву - - Указатели в C++ — это единственный способ динамического выделения памяти - - Их можно использовать для передачи функции в качестве параметра другой функци - - Их можно использовать для достижения полиморфизма при работе с наследованием - - Их можно использовать, чтобы иметь указатель на одну структуру/класс в другой структуре/классе, чтобы сформировать цепочку. Это полезно в некоторых более сложных структурах данных, таких как связанные списки и деревья - - ## Выводы - - В этом уроке мы познакомились с указателями, узнали как их объявлять, как присваивать им значения и как безопасно работать с ними. - -instructions: | - Поменяйте значения переменных `first_num` и `second_num` местами. Попробуйте это сделать с помощью уже созданных указателей. - -tips: - - | - [Указатели](https://learn.microsoft.com/ru-ru/cpp/cpp/pointers-cpp?view=msvc-170) diff --git a/modules/45-ref-and-pointers/20-pointers/ru/data.yml b/modules/45-ref-and-pointers/20-pointers/ru/data.yml index 61a67e3..7a10ad0 100644 --- a/modules/45-ref-and-pointers/20-pointers/ru/data.yml +++ b/modules/45-ref-and-pointers/20-pointers/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Указатели tips: - > diff --git a/modules/50-functions/10-define-function/description.ru.yml b/modules/50-functions/10-define-function/description.ru.yml deleted file mode 100644 index 704b6ad..0000000 --- a/modules/50-functions/10-define-function/description.ru.yml +++ /dev/null @@ -1,118 +0,0 @@ ---- -name: Определение функций -theory: | - У каждой программы должна быть функция с именем `main`. С нее программа начинает выполняться при запуске. Но когда программа масштабируется, становится сложнее размещать весь код внутри этой функции. В этом случае используют функции, которые позволяют разделить программы на небольшие модульные части. Их легче организовать, тестировать и использовать. - - Большинство программ используют множество функций. Стандартная библиотека C++ поставляется с множеством уже написанных функций, которые вы можете использовать, но также часто можно писать и свои собственные. Функции, которые вы пишете сами, называются пользовательскими функциями. - - В этом уроке мы рассмотрим как создавать пользовательские функции. - - ### Что такое функция - - Представим, что мы читаем книгу и вспоминаем, что нам нужно сделать звонок. Мы оставляем закладку, говорим по телефону и возвращаемся читать книгу с того места, где оставили закладку. - - Программы на C++ могут работать так же. Программа будет последовательно выполнять инструкции внутри одной функции, пока не обнаружит вызов другой функции. - - **Вызов функции** — это выражение, которое говорит CPU прервать текущую функцию и выполнить другую. Получается, что CPU «помещает закладку» в текущую точку выполнения, а затем вызывает функцию, указанную в вызове функции. Когда вызываемая функция завершается, CPU возвращается в точку, которая отмечена закладкой, и возобновляет выполнение. - - Разберемся как определять пользовательские функции в С++. - - ### Структура функции - - Начнем с базового синтаксиса для определения пользовательской функции. В этом уроке все пользовательские функции кроме `main` будут иметь следующий вид: - - ```cpp - тип_возвращаемого_значения идентификатор(параметры функции) { // идентификатор заменяется именем вашей функции - // ваш код здесь - } - ``` - - Подробнее о различных частях этого синтаксиса поговорим в следующих уроках. На данный момент идентификатор будет просто заменен именем нашей пользовательской функции. Фигурные скобки и инструкции между ними называются **телом функции**. - - Рассмотрим пример программы, которая показывает, как определяется и вызывается новая функция: - - ```cpp - void Greating() { - std::cout << "Hello Code Basics!" << std::endl; - } - - - int main() { - std::cout << "Starting main()" << std::endl; - // Прерываем main(), вызывая функцию Greating(). - Greating(); - // эта инструкция выполняется после завершения Greating() - std::cout << "Ending main()" << std::endl; - - return 0; - } - ``` - - Эта программа создает следующий вывод: - -
-    Starting main()
-    Hello Code Basics!
-    Ending main()
-  
- - Программа начинает выполняться с начала функции `main`. Первая строка, которая будет выполняться, выводит текст `Starting main()`. Вторая строка в `main` — это вызов функции `Greating()`. Мы вызываем функцию `Greating()`, добавляя скобки к имени функции, например: `Greating()`. Если забыть про скобки, программа может не компилироваться. - - Так как мы вызвали функцию, выполнение инструкций в `main` приостанавливается. В итоге выполнение переходит к началу вызываемой функции `Greating()`. - - Первая и единственная строка в `Greating()` печатает текст `Hello Code Basics!`. Когда `Greating()` завершается, выполнение возвращается к вызывающей функции — здесь: функция `main`. Далее оно возобновляется с того места, где остановилось. Получается, следующая инструкция, которая выполняется в `main`, выводит на печать `Ending main()`. - - ### Особенность функций - - Полезная особенность функций заключается в том, что их можно вызывать более одного раза в разных частях программы. Вот программа, которая демонстрирует это: - - ```cpp - void Greating() { - std::cout << "Hello Code Basics!" << std::endl; - } - - int main() { - Greating(); - Greating(); - return 0; - } - ``` - - Обратите внимание мы вызываем функцию `Greating()` два раза. - - Эта программа создает следующий вывод: - -
-    Hello Code Basics!
-    Hello Code Basics!
-  
- - В отличие от некоторых других языков программирования в C++ функции не могут быть определены внутри других функций. Следующая программа не является корректной: - - ```cpp - int main() { - void Greating() { - std::cout << "Hello Code Basics!"; - } - Greating(); - return 0; - } - ``` - В коде выше мы попытались определить внутри функции `main()`, функцию `Greating()` и получили ошибку компиляции: - -
-    main.cpp:12:3: error: ‘PrintMoto’ was not declared in this scope
-   12 |   PrintMoto();
-      |   ^~~~~~~~~
-  
- - Это прежде всего связанно с особенностью языка и выделением памяти. - - В этом уроке мы рассмотрели структуру и создание функций. Понятие «создать функцию» имеет много синонимов: «реализовать», «определить» и даже «заимплементить» (от слова implement). Все они встречаются в повседневной практике на работе. - -instructions: | - Напишите функцию `PrintMoto()`, которая выводит на экран фразу `Spring is coming`, и вызовите ее внутри функции `main`. - -tips: - - | - [Функции в С++](https://learn.microsoft.com/ru-ru/cpp/cpp/functions-cpp?view=msvc-170) diff --git a/modules/50-functions/10-define-function/ru/data.yml b/modules/50-functions/10-define-function/ru/data.yml index 59ad414..dd39017 100644 --- a/modules/50-functions/10-define-function/ru/data.yml +++ b/modules/50-functions/10-define-function/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Определение функций tips: - > diff --git a/modules/50-functions/20-function-return/description.ru.yml b/modules/50-functions/20-function-return/description.ru.yml deleted file mode 100644 index 1eaee6a..0000000 --- a/modules/50-functions/20-function-return/description.ru.yml +++ /dev/null @@ -1,119 +0,0 @@ ---- -name: Возврат значений -theory: | - Функции, которые мы определяли в предыдущих уроках, заканчивали свою работу тем, что печатали на экран какие-то данные: - - ```cpp - void Greating() { - std::cout << "Hello Code Basics!" << std::endl; - } - - - int main() { - Greating(); - return 0; - } - ``` - - Пользы от таких функций не очень много, так как результатом их работы невозможно воспользоваться внутри программы. - - В этом уроки мы рассмотрим, как сделать наши функции полезными. - - ## Возвращаемые значения - - Когда мы пишем пользовательскую функцию, мы можем определить, будет ли она возвращать значение вызывающей стороне или нет. - - Чтобы вернуть значение вызывающей стороне, необходимы две вещи: - - * Функция должна указать, значение какого типа будет возвращено. Это делается путем установки типа возвращаемого значения функции, который является типом, определенным перед именем функции - * Внутри функции, которая будет возвращать значение, используем инструкцию `return`. Это нужно, чтобы указать конкретное значение, возвращаемое вызывающей стороне. Конкретное значение, возвращаемое функцией, называется возвращаемым значением. Когда инструкция `return` выполняется, возвращаемое значение копируется из функции обратно в вызывающую функцию. Этот процесс называется возвратом по значению - - Общий вид функции: - - ```cpp - <тип возвращаемого значения> имя функции(аргументы) { - return возвращаемое значение - } - ``` - - Рассмотрим простую функцию, которая возвращает строку. Также пример программы, которая ее вызывает: - - ```cpp - #include - #include - // функция возвращает строку, поэтому тип возвращаемого значения std::string - std::string GetDomain() { - return "Hexlet"; - } - - int main() { - std::cout << GetDomain() << std::endl; // печатает Hexlet - - return 0; - } - ``` - - Выполнение начинается с верхней части `main`. В первой же инструкции происходит вызов функции `GetDomain()`. В результате она возвращает конкретное значение **Hexlet** обратно вызывающей стороне, которое затем выводится в консоль через std::cout. - - Любой код после `return` не выполняется: - - ```cpp - int sum() { - return 2; - std::cout << "Я никогда не выполнюсь"; - } - ``` - - Возвращать можно не только конкретное значение. Так как `return` работает с выражениями, справа от него может появиться почти все что угодно. Здесь нужно руководствоваться принципами читаемости кода: - - ```cpp - #include - - std::string GetDomain() { - std::string domain { "Hexlet" }; - return domain; - } - - ``` - - Здесь мы не возвращаем переменную. Возвращается всегда значение, которое находится в этой переменной. Ниже пример с вычислениями: - - ```cpp - int sum() { - return 5 + 5; - } - ``` - - Здесь сначала будет произведено вычисление, а затем результат мы вернем из функции. - - ## Отсутствие возвращаемого значения - - Функции не обязаны возвращать значение. Чтобы сообщить компилятору, что функция не возвращает значение, используется тип возвращаемого значения `void`. - - Посмотрим на функцию `Greating()`: - - ```cpp - #include - - void Greating() { - std::cout << "Hello, Hexlet" << std::endl; - } - ``` - - Эта функция имеет тип возвращаемого значения `void`. Это указывает на то, что она не возвращает значение вызывающей стороне. - - ## Возврат значения из main - - Теперь у нас есть концептуальные инструменты, чтобы понять, как работает функция `main`. - - Когда программа выполняется, операционная система вызывает функцию `main`. Инструкции в `main` выполняются последовательно. - - Также `main` возвращает целочисленное значение — обычно 0, и программа завершается. Значение, возвращаемое из `main`, называют кодом возврата. По нему судят об успешности выполнения программы. - -instructions: | - Реализуйте функцию `SayHurrayThreeTimes()`, которая возвращает строку "hurray! hurray! hurray!". -tips: - - | - [std::string](https://en.cppreference.com/w/cpp/string/basic_string) - - | - [Завершение программы С++](https://learn.microsoft.com/ru-ru/cpp/cpp/program-termination?view=msvc-170) diff --git a/modules/50-functions/20-function-return/ru/data.yml b/modules/50-functions/20-function-return/ru/data.yml index d88686c..f50a4cb 100644 --- a/modules/50-functions/20-function-return/ru/data.yml +++ b/modules/50-functions/20-function-return/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Возврат значений tips: - | diff --git a/modules/50-functions/30-functions-parameters/description.ru.yml b/modules/50-functions/30-functions-parameters/description.ru.yml deleted file mode 100644 index b78848e..0000000 --- a/modules/50-functions/30-functions-parameters/description.ru.yml +++ /dev/null @@ -1,80 +0,0 @@ ---- -name: Параметры и аргументы функции -theory: | - Часто разработчикам нужно передавать информацию в вызываемую функцию, чтобы у нее были данные для работы. Например, если мы хотим создать функцию для сложения двух чисел, нам нужно указать этой функции, какие именно числа следует складывать при вызове. В противном случае функция не будет знать, какие значения использовать. - - Для этой цели используются параметры и аргументы функции, которые мы изучим в этом уроке. - - ## Что такое параметры и аргументы функции - - *Параметр функции* — это переменная, которая используется внутри функции. Параметры функции работают аналогично переменным, определенным внутри функции. Но у них есть одно отличие — параметры всегда инициализируются значениями, предоставленными при вызове функции. - - Параметры функции определяются в объявлении функции — они указываются в скобках после имени функции. При этом несколько параметров разделяются запятыми. - - Посмотрим на код: - - ```cpp - #include - #include - - std::string GetFullName(std::string first_name, std::string last_name) { - return first_name + " " + last_name; - } - - int main() { - std::string result { GetFullName("John", "Wik") }; - std::cout << "Full name is: " << result << std::endl; - } - ``` - - Здесь у нас есть функция `GetFullName`, которая принимает две строки в качестве параметров. Эти параметры также называются *формальными параметрами* или *аргументами функции*. Так наша функция предоставляет интерфейс, ожидая две строки. - - В функции `main` происходит вызов функции `GetFullName` с передачей фактических аргументов. Количество и типы передаваемых аргументов должны соответствовать интерфейсу функции, иначе программа не будет скомпилирована. - - Когда функция вызывается, все параметры функции создаются как переменные. При этом значение каждого из аргументов копируется в соответствующий параметр. Этот процесс называется передачей по значению. - - ## Что такое передача по значению - - Рассмотрим следующий пример: - - ```cpp - #include - - int AddOne(int num) { - return num + 1; - } - - int main() { - int number = 1; - std::cout << number << std::endl; - - int result = { AddOne(number) }; - - std::cout << "number = " << number << std::endl; - - std::cout << "result = " << result << std::endl; - } - ``` - - Эта программа создает следующий вывод: - -
-    number = 1
-    result = 2
-  
- - Обратим внимание на следующее: - - - Мы передаем переменную в вызов функции `AddOne`. При компиляции программы вместо переменной подставляется ее значение - - Значение переменной `number` не изменяется. Это происходит из-за того, что параметр передается по значению. Также при вызове функции внутри нее создается локальная переменная `num`, в которую копируется значение переменной `number`. Так функция работает с копией данных, а не с их оригиналом - - Этот процесс и называется передачей параметров в функцию по значению. - - Итак, в этом уроке мы изучили понятия параметров и аргументов функции, а также то, как передавать параметры в функцию по значению. В C++ еще существует способ передачи параметров по ссылке, о котором мы поговорим в следующих уроках. - -instructions: | - Реализуйте функцию `Remainder()`, которая принимает число типа `int` и возвращает остаток от деления на два. Остаток от деления можно взять с помощью оператора `%`. -tips: - - | - [Функции С++](https://learn.microsoft.com/ru-ru/cpp/cpp/functions-cpp?view=msvc-170) - diff --git a/modules/50-functions/30-functions-parameters/ru/data.yml b/modules/50-functions/30-functions-parameters/ru/data.yml index 947425f..e5eea7b 100644 --- a/modules/50-functions/30-functions-parameters/ru/data.yml +++ b/modules/50-functions/30-functions-parameters/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Параметры и аргументы функции tips: - > diff --git a/modules/50-functions/40-function-prototypes/description.ru.yml b/modules/50-functions/40-function-prototypes/description.ru.yml deleted file mode 100644 index 4b92602..0000000 --- a/modules/50-functions/40-function-prototypes/description.ru.yml +++ /dev/null @@ -1,68 +0,0 @@ ---- -name: Прототипы функций -theory: | - В этом уроке мы познакомимся с прототипами функций. Также узнаем, как их определять и для чего они нужны. - - ## Определение прототипа. - - Посмотрим на код: - - ```cpp - #include - #include - - int main() { - std::cout << GetAbsolutePath("main.cpp", "andrey") << std::endl; - } - - std::string GetAbsolutePath(std::string file_name, std::string user_name) { - return "home/" + usr_home_dir + "/" + file_name; - } - ``` - - На первый взгляд программа написана правильно, но при компиляции мы получим ошибку `./main.cpp:5:20: error: use of undeclared identifier GetAbsolutePath`. - - Причина, по которой эта программа не компилируется, — компилятор последовательно компилирует содержимое исходных файлов. Когда компилятор достигает вызова функции `GetAbsolutePath` в функции `main`, он не знает, что такое `GetAbsolutePath`, потому что мы определили `GetAbsolutePath` ниже ее вызова. - - Теперь посмотрим на такой пример: - - ```cpp - #include - #include - - // прототип - std::string GetAbsolutePath(std::string, std::string); - - int main() { - std::cout << GetAbsolutePath("main.cpp", "andrey") << std::endl; - } - - // определение - std::string GetAbsolutePath(std::string file_name, std::string user_name) { - return "home/" + usr_home_dir + "/" + file_name; - } - ``` - Здесь мы определили прототип функции в верхней части. Теперь программа скомпилируется и будет работать. - - Синтаксис прототипов довольно простой и похож на синтаксис определения функций: <ТИП ВОЗВРАЩАЕМОГО ЗНАЧЕНИЯ> <ИМЯ> <КОЛИЧЕСТВО И ТИП АРГУМЕНТОВ>. Поскольку прототип функции является оператором, он должен завершаться точкой с запятой. - - Прототип функции не требует предоставления имен переменных-параметров, достаточно списка типов. - - ## Зачем нужны прототипы. - - Прототип описывает интерфейс функции для компилятора. Это значит, что он сообщает компилятору, каков тип возвращаемого значения, а также количество и тип аргументов функции. - - В нашем случае прототип сообщает компилятору, что функция `GetAbsolutePath` возвращает значение типа `std::string` и принимает два аргумента такого же типа. Если программа не предоставит эти аргументы, то прототип позволит компилятору перехватить такую ошибку. - - Прототипирование функций позволяет разбивать программу на множество модулей, которые компилируются независимо друг от друга и потом собираются вместе. В этом случае компилятор может вообще не иметь доступа к коду функции во время компиляции `main()`. То же самое справедливо и в ситуации, когда функция является частью библиотеки. - - Прототипы значительно снижают вероятность допущения ошибок. - - Итак, в этом уроке мы познакомились с прототипами функций и узнали, чем они полезны. - -instructions: | - Определите прототип функции `GetLength`, которая принимает строку и возвращает ее длину. - -tips: - - | - [Прототипы функций](https://learn.microsoft.com/ru-ru/cpp/c-language/function-prototypes?view=msvc-170) diff --git a/modules/50-functions/40-function-prototypes/ru/data.yml b/modules/50-functions/40-function-prototypes/ru/data.yml index f6df447..1bde437 100644 --- a/modules/50-functions/40-function-prototypes/ru/data.yml +++ b/modules/50-functions/40-function-prototypes/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Прототипы функций tips: - > diff --git a/modules/50-functions/50-reloading-function/description.ru.yml b/modules/50-functions/50-reloading-function/description.ru.yml deleted file mode 100644 index 6983615..0000000 --- a/modules/50-functions/50-reloading-function/description.ru.yml +++ /dev/null @@ -1,92 +0,0 @@ ---- -name: Перегрузка функций -theory: | - Перегрузка функций в С++ это мощный концепт, позволяющий существенно облегчить управление уникальными именами функций. - - Рассмотрим следующую функцию: - - ```cpp - int Add(int x, int y) { - return x + y; - } - ``` - - Эта тривиальная функция складывает два целых числа и возвращает результат тоже в виде целого числа. Однако что, если нам понадобится функция, которая может складывать два числа с плавающей запятой? Функция Add() не подходит для этого, так как все аргументы с плавающей запятой будут преобразованы в целые числа, что приведет к потере дробных частей аргументов с плавающей запятой. - - Один из способов решить эту проблему - определить несколько функций с немного разными именами: - - ```cpp - int AddInteger(int x, int y) { - return x + y; - } - - double AddDouble(double x, double y) { - return x + y; - } - ``` - - Однако, чтобы достичь наилучшего результата, это потребует определения единых правил именования для аналогичных функций с разными типами параметров, а также запоминания названий всех различных вариантов функций и правильного вызова нужной из них. - - Что произойдет, когда нам понадобится функция, которая складывает три целых числа вместо двух? Управление уникальными именами для каждой функции быстро станет утомительным. - - ## Знакомство с перегрузкой функций. - - К счастью, в C++ есть элегантное решение для таких случаев. **Перегрузка функций** позволяет создавать несколько функций с одним и тем же именем, при условии, что все эти функции имеют разные параметры (или функции могут различаться иным образом). Все функции с одинаковыми именами (в одной и той же области видимости) называются **перегруженными функциями**. - - Чтобы перегрузить нашу функцию Add(), мы можем просто определить другую функцию Add(), которая принимает параметры типа double: - - ```cpp - int add(int x, int y) { - return x + y; - } - - double add(double x, double y) { - return x + y; - } - ``` - - Показанный выше код будет успешно компилироваться. Хотя можно ожидать, что эти функции приведут к конфликту имен, на самом деле это не так. Поскольку типы параметров у этих функций различаются, компилятор может различать их и рассматривать как отдельные функции, которые просто имеют общее имя. - - ## Разрешение перегрузки. - - Когда происходит вызов функции, которая была перегружена, компилятор, исходя из аргументов, используемых в вызове, пытается сопоставить этот вызов с соответствующей перегрузкой. Это называется **разрешением перегрузки**. - - Вот простой пример, демонстрирующий это: - - ```cpp - #include - - int Add(int x, int y) { - return x + y; - } - - double Add(double x, double y) { - return x + y; - } - - int main() { - std::cout << Add(1, 2); // вызывает Add(int, int) - std::cout << '\n'; - std::cout << Add(1.2, 3.4); // вызывает Add(double, double) - - return 0; - } - ``` - Показанный выше код компилируется и дает следующий результат: - -
-  3
-  4.6
- 
- - Когда мы передаем целочисленные аргументы в вызове Add(1, 2), компилятор определяет, что мы пытаемся вызвать функцию Add(int, int). А когда мы передаем аргументы с плавающей запятой в вызове Add(1.2, 3.4), компилятор определяет, что мы пытаемся вызвать функцию Add(double, double). - - Перегрузка функций предоставляет отличный способ снизить сложность программы за счет уменьшения количества имен функций, которые нужно запомнить. Ее можно и нужно использовать. - - -instructions: | - Напишите две перегруженные функции Cube, которые принимают числа и возводят их в третью степень. Одна функция должна работать с целочисленными значениями (int), а другая — с числами с плавающей точкой (float). - -tips: - - | - [Перегрузка функций](https://learn.microsoft.com/ru-ru/cpp/cpp/function-overloading?view=msvc-170) diff --git a/modules/50-functions/50-reloading-function/ru/data.yml b/modules/50-functions/50-reloading-function/ru/data.yml index 9835a47..dca6b5f 100644 --- a/modules/50-functions/50-reloading-function/ru/data.yml +++ b/modules/50-functions/50-reloading-function/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Перегрузка функций tips: - > diff --git a/modules/50-functions/60-types-overloads-function/description.ru.yml b/modules/50-functions/60-types-overloads-function/description.ru.yml deleted file mode 100644 index c30328d..0000000 --- a/modules/50-functions/60-types-overloads-function/description.ru.yml +++ /dev/null @@ -1,98 +0,0 @@ ---- -name: Виды перегрузок функций -theory: | - В предыдущем уроке мы познакомились с перегрузкой функций, которая позволяет нам создавать несколько функций с одинаковыми именами, если все эти функции имеют разные параметры. - - В этом уроке мы поговорим о том, как различаются перегруженные функции. Перегруженные функции, которые не различаются должным образом, приведут к тому, что компилятор выдаст ошибку компиляции. - - Самый простой способ дифференцировать перегрузку функций – убедиться, что каждая перегруженная функция имеет отличающийся набор параметров. - - ## Перегрузка по количеству параметров - - Перегруженные функции различаются до тех пор, пока каждая перегруженная функция имеет разное количество параметров. Например: - - ```cpp - #include - #include - - std::string GetUserData (std::string email) { - std::string full_name = email.substr(0, email.find("@")); - return "User full name: " + full_name + "\n" + "User email: " + email; - } - - std::string GetUserData (std::string first_name, std::string last_name, std::string email) { - std::string full_name = first_name + " " + last_name; - return "User full name: " + full_name + "\n" + "User email: " + email; - } - - int main() { - std::cout << GetUserData ("John", "Doe", "john@gmail.com") << std::endl; - std::cout << GetUserData ("john@gmail.com"); - } - ``` - - Результат работы программы: - -
-  User full name: John Doe
-  User email: john@gmail.com
-  User full name: john
-  User email: john@gmail.com
-  
- - Компилятор может легко сказать, что вызов функции с одним параметром `std::string` должен идти на `GetUserData(std::string)`, а вызов функции с тремя параметрами `std::string` должен идти на `GetUserData(std::string, std::string, std::string)`. - - Обратите внимание от количества параметров может меняться и поведение функции, из примера видно, что если пользователь не ввел имя и фамилию, мы попытаем получить его полное имя из почтового адреса. - - ## Перегрузка по типу параметров - - Функции также можно различать, если различаются наборы типов параметров каждой перегруженной функции. Например, различаются все следующие перегрузки: - - ```cpp - int Add(int x, int y); // целочисленная версия - double Add(double x, double y); // версия с плавающей запятой - double Add(int x, double y); // смешанная версия - double Add(double x, int y); // смешанная версия - ``` - - Поскольку псевдонимы типов (или определения `typedef`) не являются отдельными типами, перегруженные функции, использующие псевдонимы типов, не отличаются от перегрузок, использующих исходные типы. Например, все следующие перегрузки не различаются (и приведут к ошибке компиляции): - - ```cpp - typedef int height_t; // typedef - using age_t = int; // псевдоним типа - - void Print(int value); - void Print(age_t value); // не отличается от print(int) - void Print(height_t value); // не отличается от print(int) - ``` - - Для параметров, передаваемых по значению, квалификатор `const` также не учитывается. Поэтому следующие функции не считаются разными: - - ```cpp - void Print(int); - void Print(const int); // не отличается от print(int) - ``` - - Это код также вызовет ошибку компиляции. - - Тип возвращаемого значения функции не учитывается при различении перегруженных функций. - - Рассмотрим случай, когда вы хотите написать функцию, возвращающую случайное число, но вам нужна одна версия, которая вернет `int`, и другая версия, которая вернет `double`. У вас может возникнуть соблазн сделать так: - - ```cpp - int GetRandomValue(); - double GetRandomValue(); - ``` - - При компиляции такой программы возникнет ошибка и это логично, компилятор видит вызов функции `GetRandomValue()`, как понять какую функцию вызывать? - - Выход из этой ситуации дать функциям разные имена. - - В этом уроке мы узнали, что функции можно перегружать по типу передаваемых параметров и по количеству передаваемых параметров. - -instructions: | - Напишите две перегруженные функции `std::string GetFullName()`. Эти функции должны работать так: если в функцию передали имя и фамилию, то она возвращает полное имя, если в функцию не передали аргументы, она вернет строку "Anonymous". - -tips: - - | - [Перегрузка функций](https://learn.microsoft.com/ru-ru/cpp/cpp/function-overloading?view=msvc-170) diff --git a/modules/50-functions/60-types-overloads-function/ru/data.yml b/modules/50-functions/60-types-overloads-function/ru/data.yml index 250e2da..f0feba5 100644 --- a/modules/50-functions/60-types-overloads-function/ru/data.yml +++ b/modules/50-functions/60-types-overloads-function/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Виды перегрузок функций tips: - > diff --git a/modules/50-functions/70-default-arguments/description.ru.yml b/modules/50-functions/70-default-arguments/description.ru.yml deleted file mode 100644 index 55f8441..0000000 --- a/modules/50-functions/70-default-arguments/description.ru.yml +++ /dev/null @@ -1,121 +0,0 @@ ---- -name: Аргументы по умолчанию -theory: | - **Аргумент по умолчанию** – это значение по умолчанию, предоставленное для параметра функции. Если пользователь не предоставляет явный аргумент для параметра с аргументом по умолчанию, то будет использоваться значение по умолчанию. Если пользователь предоставляет аргумент для этого параметра, то используется аргумент, предоставленный пользователем. - - Поскольку пользователь может выбрать, следует ли предоставить конкретное значение аргумента или использовать значение по умолчанию, параметр с предоставленным значением по умолчанию часто называется необязательным параметром. - - Посмотрим на следующий код: - - ```cpp - #include - #include // библиотека для работы с файлами - - void CreateConfigFile(std::string file_path = "/usr/local/nginx/conf") { - std::ofstream Config(file_path); // создаем конфигурационный фаил - Config.close(); // и тут же закрываем его - } - ``` - - У нас есть функция, которая создает пустой конфигурационный файл для сервера. Если вызывающая сторона не передаст в функцию путь, то будет использован путь по умолчанию `/usr/local/nginx/conf`. - - ## Несколько аргументов по умолчанию - - Функция может иметь несколько параметров с аргументами по умолчанию: - - ```cpp - #include - #include - #include - - void CreateConfigFile(std::string file_path = "/usr/local/nginx/conf", int port = 80, std::string protocol = "https") { - std::ofstream Config(file_path); - Config << port << std::endl; - Config << protocol; - Config.close(); - } - ``` - - Если вызвать функцию `CreateConfigFile()` без аргументов, то будет создан конфигурационный файл по пути `/usr/local/nginx/conf` и в него будет записан номер порта `80`, протокол `https`. - - Обратите внимание, что невозможно предоставить аргумент для параметра `protocol` без предоставления аргументов для параметров `file_path` и `port`. Это связано с тем, что C++ не поддерживает синтаксис вызова функции, такой как CreateConfigFile( , ,"http"). Это имеет два основных последствия: - - - Все аргументы по умолчанию должны быть для крайних правых параметров. Запрещено следующее: - - ```cpp - void CreateConfigFile(std::string file_path, int port, std::string protocol = "https") - ``` - - - Если существует более одного аргумента по умолчанию, крайний левый аргумент по умолчанию должен быть тем, который с наибольшей вероятностью будет явно установлен пользователем. - - Аргументы по умолчанию могут быть объявлены только один раз, то есть аргумент по умолчанию может быть объявлен либо в прототипе функции, либо в определении, но не в обеих сразу. - - Такой код вызовет ошибку: - - ```cpp - void CreateConfigFile(std::string file_path = "/usr/local/nginx/conf", int port = 80, std::string protocol = "https") - - void CreateConfigFile(std::string file_path = "/usr/local/nginx/conf", int port = 80, std::string protocol = "https") { - std::ofstream Config(file_path); - Config << port << std::endl; - Config << protocol; - Config.close(); - } - ``` - - Лучше всего объявлять аргумент по умолчанию в предварительном объявлении, а не в определении функции, поскольку предварительное объявление с большей вероятностью будет замечено другими файлами (особенно если оно находится в заголовочном файле). - - ## Аргументы по умолчанию и перегрузка функций - - Функции с аргументами по умолчанию могут быть перегружены. Например, разрешено следующее: - - ```cpp - void CreateConfigFile(std::string file_path, int port); - void CreateConfigFile(std::string file_path, std::string port = "80"); - ``` - - Теперь если вызвать функцию `CreateConfigFile("/usr/local/nginx/conf")`, то в конфигурационный файл будет записано значение порта `80`. - - Однако важно отметить, что необязательные параметры не учитываются в отношении параметров, которые делают функцию уникальной. Следовательно, следующее недопустимо: - - ```cpp - void CreateConfigFile(std::string file_path); - void CreateConfigFile(std::string file_path, std::string port = "80"); - ``` - - Компилятор не сможет разрешить эту перегрузку, потому что при вызове функции `CreateConfigFile()` возникнет неоднозначность. - - Аргументы по умолчанию предоставляют полезный механизм для указания параметров, для которых пользователю необязательно указывать значения. - - -instructions: | - Напишите функцию `GetHiddenCard()`, которая принимает в качестве первого параметра номер кредитной карты(16 символов) в виде строки и возвращает его скрытую версию. Если исходная карта имела номер 2034399002125581, то скрытая версия выглядит так ****5581. Другими словами, функция заменяет первые 12 символов, на звездочки. Количество звездочек регулируется вторым необязательным параметром. Значение по умолчанию — 4. - - ```cpp - GetHiddenCard("2034399002121100", 1); // "*1100" - GetHiddenCard("1234567812345678", 2); // "**5678" - GetHiddenCard("1234567812345678", 3); // "***5678" - GetHiddenCard("1234567812345678"); // "****5678" - ``` - - Для извлечения подстроки воспользуйтесь методом `substr()`: - - ```cpp - int main() { - std::string str = "2034399002121100"; - - std::cout << str.substr(str.length() - 4, 4); // 1100 - } - ``` - - Для повторения строки указанное количество раз воспользуйтесь `std::string`: - - ```cpp - int main() { - std::cout << std::string(4, '*') // **** - } - ``` - -tips: - - | - [Аргументы по умолчанию](https://learn.microsoft.com/ru-ru/cpp/cpp/default-arguments?view=msvc-170) diff --git a/modules/50-functions/70-default-arguments/ru/data.yml b/modules/50-functions/70-default-arguments/ru/data.yml index 92281d6..4486c9c 100644 --- a/modules/50-functions/70-default-arguments/ru/data.yml +++ b/modules/50-functions/70-default-arguments/ru/data.yml @@ -1,3 +1,4 @@ +--- name: Аргументы по умолчанию tips: - > From c8db250f2912aa790606bd75c9ade7e9aa8a7d6a Mon Sep 17 00:00:00 2001 From: ola-9 Date: Fri, 21 Jun 2024 16:01:01 +0300 Subject: [PATCH 2/2] fix lint --- modules/10-basics/description.ru.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/10-basics/description.ru.yml b/modules/10-basics/description.ru.yml index d00f141..723f60f 100644 --- a/modules/10-basics/description.ru.yml +++ b/modules/10-basics/description.ru.yml @@ -2,4 +2,3 @@ name: Основы description: | C++ – язык программирования созданный програмистом Bell Labs Бьёрн Страуструп в начале 80-х годов. Он стабильно входит в Top 10 самых популярных языков. На нем можно разрабатывать realtime или высокопроизводительные приложения, игры, мультимедиа, системное и индустриальное ПО. Изучать C++ мы будем с нуля, с самых азов. Первый модуль – плацдарм для написания осмысленных программ. В нем мы разберем, как написать свой первый код на C++. Расскажем, что такое комментарии и зачем они нужны. На примере проверки ваших решений рассмотрим, что такое тестирование и как читать вывод тестов. -