Начинаем изучать язык программирования C. CS50 на русском. Лекция 1.1
Дэвид Малан рассказывает об основах языка С, о хорошем и плохом коде, о работе с Visual Studio Code и разбирает простую программу на C.
![](https://248006.selcdn.ru/main/iblock/fc8/fc884dd41ab16be2fa92d35de3a8ba67/94d8be658313b637bab0549e2dd47e8b.jpg)
![](https://248006.selcdn.ru/main/iblock/fc8/fc884dd41ab16be2fa92d35de3a8ba67/94d8be658313b637bab0549e2dd47e8b.jpg)
Кадр: фильм «Переводчики»
CS50 (Computer Science 50) — легендарный курс о компьютерных технологиях Гарвардского и Йельского университетов. Практически в любом разговоре о том, как вкатиться в программирование, опытные ребята упоминают этот курс как необходимую базу. В нём последовательно разбираются логика работы компьютера, работа с визуальным программированием в Scratch, основы языка C, массивы, основные алгоритмы, работа памяти, структуры данных, основы языка Python, основы SQL, HTML, CSS, JavaScript, Python-фреймворк Flask и даже эмодзи :)
Изучив материалы курса, вы разберётесь в том, как работает компьютер, узнаете универсальные принципы программирования и сможете понимать и читать код, написанный на разных языках.
В этой статье мы разберём основы программирования на примере визуальной среды Scratch — хардкор, C и прочие штуки начнутся со следующей статьи цикла.
Содержание
- Главные условия создания хорошего кода
- Использование IDE
- Компилирование программы
- Функции и аргументы
- Зачем нужна функция main
- Заголовочные файлы (header files)
- Команды Linux
Почему мы перевели CS50 и как устроена каждая статья по курсу
CS50 — это самый популярный курс в Гарвардском университете и самый посещаемый массовый открытый онлайн-курс на edX. Все материалы курса доступны бесплатно (в том числе и практические задания), но, если заплатить, можно получить сертификат и разные дополнительные плюшки.
Мы перевели видеолекции в текстовый формат, снабдили их иллюстрациями, кое-где дополнили объяснения и выкладываем в открытый доступ. Оригинальный курс доступен по лицензии Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) — его можно дорабатывать и распространять бесплатно, но только под исходной лицензией. Так что этот цикл материалов вы также сможете использовать в своей работе или общественной деятельности совершенно свободно и бесплатно в рамках той же лицензии.
Каждая статья из цикла CS50 состоит из следующих материалов:
- текстовый перевод видео (иногда — половины видео, если тема обширная);
- ссылка на оригинальное видео на английском языке;
- схемы и пояснения;
- ссылки на более подробные материалы по теме статьи;
- практические задания.
![](/upload/setka_images/13362717072023_dfaaeed694eae97c85b434d102c0fe199eb9c461.png)
Дэвид Дж. Малан
Американский учёный, профессор Гарвардского университета.
Оригинальное видео
Главные условия создания хорошего кода
На этой лекции мы начнём изучать новый язык — C. В отличие от графического Scratch, это традиционный текстовый язык, лежащий в основе многих современных языков, например того же Python.
Хочу напомнить, что наша цель не в том, чтобы выучили Scratch, C или даже Python, а в том, чтобы научиться программировать. В языке С много деталей, которые могут отвлекать от процесса программирования: синтаксис, странная пунктуация — все эти решётки, угловые, круглые и фигурные скобки, обратная косая черта и многое другое. Но я обещаю, что скоро вы со всем этим разберётесь. Для этого нужно вспомнить, что мы изучали.
Итак, выполняя задачи на Scratch, мы рассмотрели функции, у которых есть аргументы (входные данные), а также возвращаемые значения (выходные данные) — хотя последние есть не у всех функций.
Затем мы немного поговорили об условных выражениях — так сказать, дорожных развилках, — а также о логических выражениях, представляющих собой вопросы с двумя вариантами ответа: да/нет или истина/ложь.
Мы рассмотрели циклы, которые позволяют повторять какое-то действие снова и снова, переменные, временно сохраняющие значения и тому подобное. Всё это нам пригодится.
Теперь поговорим о том, как переводить эти идеи на языки программирования. Синтаксис языков программирования проще, чем синтаксис естественного, человеческого языка, ведь и слов в языках программирования гораздо меньше. Однако с языками программирования нужно быть более аккуратными — компьютер не прощает даже малейших ошибок.
Итак, вы написали код и спрашиваете себя: хорош ли он? Как же это понять? Самый первый критерий — код должен правильно решать поставленную задачу. Но, даже если вы правильно решили задачу, а при этом ваш код длинный и беспорядочный, он будет считаться плохим. Другому программисту будет сложно понять, что ваш код делает и действительно ли он правильный. Да и вы сами, когда посмотрите на этот код в следующем году, а то и на следующее утро, возможно, не поймёте, что написали.
В итоге программисту необходимо сосредоточиться на разработке хорошего кода, на повышении эффективности алгоритмов, на том, чтобы код чистым и даже выглядел красиво. Но это уже вопрос стиля.
Итак, у хорошего кода есть ряд характеристик: корректность, дизайн (архитектура, структура), стиль. А теперь разберём программу на С, которая печатает в терминале hello, world.
#include <stdio.h>
int main(void)
{
printf("hello, world\n");
}
Честно говоря, это довольно жестоко — столько раз нажимать на клавиши, чтобы получить такой незначительный результат. Через несколько лекций, когда мы представим другие, более современные языки, такие как Python, вы сможете преобразовать эту логику буквально в одну строку кода. Но сейчас будет полезно понять, что здесь происходит.
Использование IDE
Итак, как напечатать hello, world на экране? Я мог бы открыть Word, Google Docs или Pages, набрать текст символ за символом, сохранить его — и вот у меня уже есть готовая программа. Но проблема в том, что компьютеры понимают только двоичный код, нули и единицы. Ни один из тех инструментов, которые я перечислил, не подходит для программирования — у них нет возможности преобразовать этот текст в двоичный код.
Но есть инструменты, которые дают такую возможность. Они называются интегрированными средами разработки, или IDE. Вот, например, как выглядит очень популярная Visual Studio Code (VS Code).
![](/upload/setka_images/13391717072023_5c20dcbcfbab07ab6c2df7e27444d5ac2afca569.png)
Кадр: CS50 / YouTube
Здесь вверху вы видите, как я создал пустой файл под названием hello.c, где .c указывает, что это файл с кодом на языке программирования C. Цифра 1 слева — это автоматически проставляется нумерация строк. Так удобнее ориентироваться в коде программы.
Теперь я добавлю кое-что под названием stdio.h (подробнее об этом поговорим позже), а затем наберу int main(void) (к этому мы тоже вернёмся далее), затем я введу фигурные скобки и нажму клавишу Tab, чтобы сделать отступ на несколько пробелов. В завершение я наберу printf(), "hello, world/n" и поставлю точку с запятой.
![](/upload/setka_images/13400517072023_278cadb5c5a600fd354bbb4a32acf34407bf98f0.png)
Кадр: CS50 / YouTube
Теперь нужно нажать Ctrl + S — это сохранит файл. И вуаля, я уже программист.
Компилирование программы
Моя программа находится в файле под названием hello.c. Теперь мне нужно преобразовать исходный код — то есть текст программы на языке программирования, как его принято называть, в нули и единицы, которые мы будем называть машинным кодом. Его как раз и выполняет компьютер.
Конечно, я не буду делать это вручную. Есть алгоритм, реализованный специальной программой, которая делает такое преобразование — эта программа называется компилятором. Не все языки его используют, но C — язык, использующий компилятор.
Итак, в верхней части экрана находится мой текстовый редактор, в котором я могу запросто создавать файлы и писать код. Посмотрите на нижнюю часть экрана — там есть область, которая называется окном терминала. Там я могу вводить и запускать команды, например скомпилировать свой исходный код в машинный код.
Итак, как же мне превратить свой файл hello.c в программу? Для этого я ввожу в командной строке терминала команду make hello и нажимаю Enter, а затем ввожу ./hello и опять нажимаю Enter.
![](/upload/setka_images/13411017072023_bd473197c461193ea9b6d317f4c236910d065887.png)
Кадр: CS50 / YouTube
make hello осуществляет сборку и компиляцию программы, а ./hello означает запуск программы под названием hello в текущей папке.
Теперь я открою боковую панель IDE.
![](/upload/setka_images/13411017072023_e3039f248dd555899a396179b51a05be377f9973.png)
Кадр: CS50 / YouTube
То, что я открыл, называется Explorer. Здесь можно увидеть все файлы своей учётной записи. Один из них называется hello.c — он выделен, потому что открыт прямо здесь, в нашей IDE. Другой называется hello — это новый файл, созданный при компиляции. Файл hello — исполняемый, то есть его можно запускать.
Теперь, если мы хотим изменить программу, например, чтобы она печатала hello, CS50, нам нужно не только исправить код в файле hello.c, но и скомпилировать его заново, чтобы получить обновленный файл hello. Только тогда результат вывода программы изменится.
Функции и аргументы
А сейчас вспомним, что такое функции и аргументы. Функции — это действия, такие как «говорить», «спрашивать» и тому подобное. Аргументы функций в Scratch вводились, как правило, в маленьких белых овалах.
Давайте рассмотрим нашу программу на С в контексте Scratch. Для вывода текста на экран здесь используется функция printf. Круглые скобки заменяют белый овал. Ещё есть двойные кавычки, которых не было в Scratch — они нужны, если в качестве аргумента функции, то есть данных, которые функция обрабатывает, используется строка. Ещё в коде есть точка с запятой — в программировании на языке C она используется, чтобы «закончить свою мысль».
У функций могут быть разные типы выходных данных — то есть результат. Чаще всего это какие-то строки, символы, числа и тому подобное. Но выходными данными может быть и некий «побочный эффект» — обычно это что-то, что вы видите на экране или что можете услышать: изображения, звук, какой-то появляющийся текст.
В Scratch у нас были функции, которые, помимо побочных эффектов, возвращали какое-то значение. Например, блок Спросить, который сохранял ответ на вопрос «Как вас зовут?» в специальной переменной Ответ. Такой результат вы могли использовать повторно — в отличие от побочного эффекта, который появляется на экране и исчезает.
Самое близкое совпадение, которое я могу предложить для блока Спросить — это функция get_string. Она также возвращает полученный результат в виде переменной, и его можно использовать повторно.
![](/upload/setka_images/13411017072023_ee673444daa2c4c150863fb4fe2e59385df85324.png)
Кадр: CS50 / YouTube
В отличие от математики, знак = в программировании на C не означает равенство — это оператор присваивания. Переменной answer присваивается результат выполнения функции. А сама функция предлагает пользователю ввести какое-то значение.
Кроме того, в C вы не просто даёте имя переменной, как в Scratch. Вам нужно заранее сообщить компьютеру, какой тип данных она будет хранить. Тип string используется для строк, а int — для целых чисел. Есть и другие типы данных — мы изучим их позже.
Если в программе указано, что переменная имеет тип string, то компьютер будет интерпретировать хранящиеся в ней нули и единицы как слова или последовательность букв (даже пустую последовательность букв), а если это int — как целое число. Если я попытаюсь поместить строку в число или число в строку, команда make выдаст сообщение об ошибке (но тут важно понимать, что все данные, которые вы вводите сами в терминал, — будь то число или текст, программа будет понимать именно как текст, строку, тип string).
Типы данных в C:
- bool — булевы значения (истина/ложь);
- char — единичный символ;
- float — числа с плавающей точкой/запятой (дробные числа);
- double — более длинные числа с плавающей точкой/запятой (дробные числа);
- int — целые числа;
- long — большие целые числа;
- string — строковые данные.
Эти типы помогают «понимать» и обрабатывать последовательности единичек и нулей как осмысленные данные конкретного формата.
Теперь разберёмся с \n. Перейдём к VS Code и вернёмся к программе "hello, world". Уберём \n и запустим код на компиляцию и выполнение.
![](/upload/setka_images/13411017072023_a3e9b924b0c79cb7169afa563a255fa0a5b1cadd.png)
Кадр: CS50 / YouTube
Как видим, знак $ остался на той же строке, что и "hello, world". Мы можем сделать вывод, что \n — это обозначение для перемещения курсора на следующую строку. Без него выводимый текст выглядит неаккуратно.
А теперь сделаем программу более интерактивной. Пусть она сначала спрашивает: «Как вас зовут?», а потом здоровается с человеком.
Обновлённая программа на С будет выглядеть так:
![](/upload/setka_images/13411117072023_cae856732bd4226855875d839121e46dd85999a9.png)
Кадр: CS50 / YouTube
#include <cs50.h>
#include <stdio.h>
int main(void)
{
string answer = get_string("What's your name? ");
printf("hello, %s\n", answer);
}
Разберёмся в тексте (листинге) этой программы. Я включил сюда ещё одну строку кода, точнее, библиотеку, которая называется cs50.h. Дело в том, что некоторые функции C поставляются в комплекте с языком. Но чаще всего, если вы хотите использовать функцию, вы должны загрузить библиотеку, которая её содержит. get_string — это функция, которую некоторое время назад написали для нашего курса CS50. Она позволяет легко получать информацию от пользователя из терминала — если бы не она, нам бы пришлось писать очень мудрёный код. Чтобы использовать эту функцию, нужно сказать программе, чтобы она загрузила и использовала библиотеку cs50.h, в состав которой и входит функция get_string.
Библиотека — это что-то вроде заранее написанных другими программистами полезных кусочков кода, к которым можно обращаться как к функциям с помощью коротких имён — в нашем случае get_string. stdio.h — это так называемая «стандартная библиотека», которая идёт в комплекте с языком программирования. Да, верно — даже за базовыми функциями языка программирования есть чей-то заранее написанный код.
Итак, в нашей программе есть две библиотеки:
- stdio.h — библиотека, которая даёт вам доступ к printf и другим функциям, связанным с вводом и выводом.
- cs50.h — библиотека, предоставляющая доступ к функциям, которых нет в стандартной библиотеке C, и включающая в себя get_string.
Какие функции включены в состав библиотеки CS50:
- get_char()
- get_double()
- get_float()
- get_int()
- get_long()
- get_string()
Эти функции помогают ввести в программу данные того или иного типа из командной строки. Если вам нужна строка, используйте функцию get_string() — она попросит ввести в терминал какой-то текст и присвоит его определённой переменной (в этом случае и число будет считаться текстом, строковым параметром).
А что же означает знак %s? В Scratch, если нам нужно было объединить две строки, мы просто писали «яблоко» и «банан». В языке С другой синтаксис. Вы помещаете в строке внутри двойных кавычек последовательность символов %s, которая говорит компьютеру, что на это место надо поставить переменную, название которой будет указано после кавычек, но внутри круглых скобок. В нашем случае это переменная answer.
Кроме %s есть и другие специальные последователи символов:
- %s — string;
- %i — integer;
- %f — float и double;
- %li — long integer;
- %c — char.
Синтаксис вы помните: даём строку в кавычках, внутри неё используем %s, а после строки пишем имя переменной, из которой надо взять значение и подставить вместо %s.
Итак, сколько аргументов (то есть входных данных) принимает функция printf? Правильный ответ — два аргумента. Запятая после кавычек отделяет первый аргумент — строку в кавычках от второго — переменной answer. Запятая внутри кавычек является частью аргумента типа string, то есть частью строки.
Я хочу указать на красивые цвета, которые подсвечивают текст в окне редактирования. Так, например, переменная string_answer здесь выделена чёрным цветом, функция get_string — коричнево-жёлтым, %s — синим. Это редактор VS Code выделяет для вас синтаксис — такая функция называется «подсветка синтаксиса». Благодаря этому программист может различать разные сущности по цветам и более просто ориентироваться в тексте программы. Видите чёрное — ага, это точно переменная. Видите коричнево-жёлтое — это явно функция. Подсветка синтаксиса — это функциональность, которую предоставляют практически все редакторы кода и IDE.
Обратите внимание на отступ в строках 6 и 7. Я настроил VS Code на отступ в четыре пробела, появляющиеся каждый раз, когда я нажимаю клавишу Tab, — это распространённое соглашение. Отступы помогают подчеркнуть иерархию элементов, их вложенность. Если элементы на одном уровне — они начинаются с одинакового отступа (например, открывающая фигурная скобка и закрывающая фигурная скобка). А если один элемент вложен в другой, он начинается с отступа относительно родительского элемента. Например, объявление переменной string_answer вложено в функцию main.
Операторы, инкременты, декременты и объявление переменных
В языке C существует много операторов для математических вычислений, а сами эти вычисления происходят на каждом шагу. Вот все операторы:
- + — плюс;
- - — минус;
- * — умножение;
- / — деление;
- % — даёт остаток от деления одного числа на другое.
Также в C есть переменные. Мы уже видели их. А ещё есть синтаксический сахар — это такие выражения, которые позволяют очень просто выразить сложную мысль в коде.
Когда мы создаём переменные, нам надо их объявить — указать, какого они типа, и в конце поставить точку с запятой. Для примера создадим переменную counter, то есть простой счётчик. Помните, такой счётчик у нас был в Scratch? Только он выглядел гораздо проще.
int counter = 0;
- int — указатель типа переменной;
- counter — имя переменной;
- = — оператор присваивания какого-либо значения (это не «равно», а именно оператор присваивания, он не просто говорит, что какие-то значения равны, он именно присваивает левой части выражения значение из правой части);
- 0 — значение, которое мы присваиваем переменной;
- ; — окончание практически для любой смысловой единицы в C.
В Scratch есть удобный визуальный блок, который позволяет создать счётчик, значение которого бы обновлялось при совершении какого-то действия — увеличивалось на единицу. В C, чтобы увеличить значение счётчика, можно написать следующий код:
counter = counter + 1$
Заметим снова: знак «равно» здесь — не знак «равно», а оператор присваивания. Если бы это был знак «равно», само выражение не имело бы смысла. Вообще, операция увеличения счётчика на один — очень распространённая в программировании (это ещё называется «инкремент»). И сейчас мы покажем, что такое синтаксический сахар в действии. Следите за руками:
counter += 1;
Оно означает, что counter равен самому себе плюс единице. А вот ещё один способ сделать это гораздо проще:
counter++;
Те же самые операции можно проводить и с оператором — (такая операция будет называться «декремент»).
Важное примечание: когда мы меняем значение уже созданной переменной, нам не надо снова указывать её тип, то есть мы не пишем так:
int counter += 1;
Зачем нужна функция main
Давайте вернёмся к тому, с чего мы начали. Зачем нужна строка int main(void)?
Это стандартное начало программ на С. Строка int main(void) сообщает компилятору, что есть функция с именем main (главная функция), и она возвращает целое число типа int. Слово void показывает, что у нашей функции нет аргументов (void переводится как «пустота»).
Фигурные скобки показывают начало { и конец } тела функции. Они используются и в других структурных блоках кода, но обозначают всегда одно — начало и конец какой-то сущности внутри программы.
Когда будет достигнут конец главной функции (закрывающая фигурная скобка), программа автоматически вернёт значение 0 (именно поэтому тип нашей функции — int, то есть целое число). Это важное значение — проанализировав его, операционная система может понять, успешно завершилась наша программа или нет. Возвращаемое значение 0 означает успешный успех.
Заголовочные файлы (header files)
Нырнём немного глубже и разберёмся, что такое заголовочные файлы. Когда мы изучали Scratch, я их не упоминал: мы просто выставляли зелёный флаг на клик и потом выводили "hello, world" — всё очень просто и наглядно.
Однако в языке C вы не можете просто написать функцию main() со вложенной функцией printf(). Вам придётся написать в первой строке программы #include <stdio.h>, чтобы сказать компилятору: «Загрузи библиотеку кода, которую написал кто-то до меня» — только так компилятор узнает, что вообще такое printf().
Например, вам надо будет загрузить библиотеку CS50, чтобы использовать функции get_string(), get_int() и тому подобные (далее мы поговорим и о них). В общем, без такой строки сам компилятор никогда не узнает, что такое get_string(). А файлы типа stdio.h или cs50.h среди программистов на C и C++ принято называть заголовочными файлами. Чуть позже мы посмотрим, что находится внутри этих файлов, но если кратко, то это как ресторанное меню, где вместо блюд — все доступные функции. И такое «меню» помогает компилятору узнать, как правильно использовать и понимать все эти функции.
Важное различие: библиотека предоставляет все те функции, о которых мы говорили выше, а заголовочный файл — это лишь специальный механизм, благодаря которому вы можете включить нужные функции в свой код. То есть библиотека будет называться CS50 или Standard I/O, а соответствующие им заголовочные файлы будут называться stdio.h, cs50.h.
Команды Linux
Теперь перейдём на вкладку Explorer IDE и посмотрим, как устроен наш программный проект. Как видно на скриншоте, там у нас лежат два файла: hello и hello.c.
![](/upload/setka_images/13411117072023_073efc852a65b7685aeef7707c1c1bd107b26868.png)
Кадр: CS50 / YouTube
А теперь представьте, что нам надо как-то организовать свой код — например, для занятий студентов разных групп или с разными учебными программами на C. В принципе, в графическом интерфейсе мы можем сделать всё, что нам нужно, используя знакомые и привычные иконки macOS, Windows или Linux. С их помощью можно нажать на иконку добавления каталога и назвать новый каталог так, как нам надо.
Подробная справка по командной строке Linux — в нашей статье.
Однако то же самое можно сделать с помощью командной строки. Например, введя в командном интерфейсе ls, я могу просмотреть содержимое проекта (названия со слешем / на конце — это как раз папки, а не обычные файлы). С помощью команды mkdir (MaKe DIRectory) можно создать и назвать новую директорию.
![](/upload/setka_images/13411017072023_d0e289e355555cb39f9d7f499b6888c389473c54.png)
Кадр: CS50 / YouTube
clean в командном режиме позволяет стереть историю — то есть все команды, которые вы набирали в командной строке ранее. На практике это делается редко, и в рамках курса я ввожу clean, чтобы держать в фокусе только свои последние набранные команды.
mkdir создаст новую директорию. После этой команды надо написать название новой директории. Например, mkdir pset1 создаст каталог для кода первой недели, а mkdir pset2 — для второй.
Теперь я хочу перейти в одну из вновь созданных директорий. Для этого в командной строке я введу команду cd pset1. cd означает Change Directory. После ввода этой команды я нажимаю клавишу Enter и тут же происходят две примечательные вещи: строка приглашения к набору команд меняется на имя той директории, в которую я только что перешёл — всегда напоминая мне, где я в данный момент работаю.
После этого я могу просмотреть содержимое каталога с помощью команды ls. Если ввести её в текущем каталоге, она выведет список файлов (в нашем случае ничего — потому что в папке пока пусто).
Теперь попробуем создать в папке новый файл — командой code my_first_program.c. Этот файл тут же появится на новой вкладке вашего редактора. Если же мы хотим перейти на один каталог выше, надо ввести cd .. — эта команда автоматически поднимет вас в родительский каталог. А cd ../.. — переведёт вас на два каталога выше. ./ означает «текущий каталог». В общем, командная строка может делать всё, что умеет GUI, но не наоборот. Напомним, это команды терминала, они не имеют отношения к языку C.
Итоги
Ну что ж, на сегодня хватит — а в следующем уроке мы ещё сильнее погрузимся в программирование на C, напишем простенькие программки и попрактикуемся. Подведём небольшие итоги:
- C — это компилируемый язык программирования. Прежде чем программа на C начнёт выполняться, её необходимо полностью перевести в единички и нули, то есть сделать понятной для компьютера.
- Программировать удобно в средах разработки (IDE) или редакторах для программирования. мы использовали VS Code.
- Вместо того чтобы создавать и просматривать папки обычным способом — с помощью иконок, можно использовать командную строку.
- Переменные — это такие области памяти компьютера, в котором хранятся какие-то данные. У этих данных есть тип — строка, число и тому подобные.
- В языке C существуют операторы, функции, заголовочные файлы и библиотеки.
- main() — самая главная функция, с которой начинается программа.
Больше интересного про код — в нашем телеграм-канале. Подписывайтесь!