C++ 1MIT весна 1 2020 — различия между версиями

Материал из CSC Wiki
Перейти к:навигация, поиск
(Прочие материалы)
(Практика 1: Соколов)
Строка 57: Строка 57:
  
 
Trac: <code>sokolov</code>
 
Trac: <code>sokolov</code>
 
'''deadline первой попытки первой лабораторной: среда 09.11 21:00'''
 
  
 
Письма просьба идентифицировать префиксом в теме <code>[HSE][CPP]</code>
 
Письма просьба идентифицировать префиксом в теме <code>[HSE][CPP]</code>
  
 
==== Linux ====
 
==== Linux ====
* Минимальный набор для работы из консоли (для Ubuntu):
 
** <code>man</code> manual
 
** <code>ls</code> list
 
** <code>pwd</code> print working directory
 
** <code>mkdir</code> make (create) directory
 
** <code>cd</code> change directory
 
** <code>cp</code> copy
 
** <code>rm</code> remove
 
** <code>touch</code> "потрогать" объект на файловой системе
 
** <code>mv</code> move
 
** <code>gcc</code> gnu C compiler
 
** <code>make</code> make
 
** <code>svn</code> subversion
 
** <code>apt</code> aptitude
 
 
 
* Текстовые редакторы
 
* Текстовые редакторы
 
** <code>subl</code> [https://www.sublimetext.com Sublime text]
 
** <code>subl</code> [https://www.sublimetext.com Sublime text]
Строка 123: Строка 106:
  
 
==== Полезные сайты ====
 
==== Полезные сайты ====
* [https://en.cppreference.com/w/c cppreference] - консультация со стандартом языка Си
 
 
* [https://godbolt.org godbolt] - compiler explorer (удобно делиться примерами)
 
* [https://godbolt.org godbolt] - compiler explorer (удобно делиться примерами)
 
* [http://ideone.com ideone] - возможность делиться кодом, поддержка разных ЯП, исполнение кода online
 
* [http://ideone.com ideone] - возможность делиться кодом, поддержка разных ЯП, исполнение кода online
 
* [https://stackoverflow.com stackoverflow] - огромное количество ответов на разные вопросы. Прежде чем самому задавать вопрос, будет полезно ознакомиться с текстом https://habr.com/ru/post/339038/  
 
* [https://stackoverflow.com stackoverflow] - огромное количество ответов на разные вопросы. Прежде чем самому задавать вопрос, будет полезно ознакомиться с текстом https://habr.com/ru/post/339038/  
* [https://cdecl.org cdecl] - поможет ответить на вопрос, что есть <code>int (*(*foo)(void ))[3]</code>
 
 
* [https://github.com/nothings/single_file_libs] - список библиотек из 1-2 файлов
 
* [https://github.com/nothings/single_file_libs] - список библиотек из 1-2 файлов
* [https://en.cppreference.com/w/c/links/libs] - список open-source библиотек на языке Си
 
 
* [https://en.cppreference.com/w/cpp/links/libs] - список open-source библиотек на языке Си++
 
* [https://en.cppreference.com/w/cpp/links/libs] - список open-source библиотек на языке Си++
 
* [https://en.cppreference.com/w/cpp/language/history] - история языка Си++
 
* [https://en.cppreference.com/w/cpp/language/history] - история языка Си++
  
 
==== Статьи ====
 
==== Статьи ====
* [https://en.cppreference.com/w/c/language/behavior UB в языке C]
 
 
* [https://en.cppreference.com/w/cpp/language/ub UB в языке C++]
 
* [https://en.cppreference.com/w/cpp/language/ub UB в языке C++]
 
* [http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r0.html integer overflow proposal] - предложение в Стандарт языка C++: integer overflow - не UB.
 
* [http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r0.html integer overflow proposal] - предложение в Стандарт языка C++: integer overflow - не UB.
Строка 140: Строка 119:
 
* [http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1705r0.html попытка решить проблему UB в языке C++]
 
* [http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1705r0.html попытка решить проблему UB в языке C++]
 
* [https://stackoverflow.com/questions/16979791/c-data-structures-alignment обсуждение про выравнивание]
 
* [https://stackoverflow.com/questions/16979791/c-data-structures-alignment обсуждение про выравнивание]
* [http://www.kroah.com/log/linux/container_of.html the weird Linux kernel container_of macro]
 
 
* [https://ru.wikipedia.org/wiki/%D0%90%D0%BD%D1%82%D0%B8%D0%BF%D0%B0%D1%82%D1%82%D0%B5%D1%80%D0%BD Антипаттерны]
 
* [https://ru.wikipedia.org/wiki/%D0%90%D0%BD%D1%82%D0%B8%D0%BF%D0%B0%D1%82%D1%82%D0%B5%D1%80%D0%BD Антипаттерны]
 
* [https://habr.com/ru/company/pvs-studio/blog/348098/ Почему важно проверять, что вернула функция malloc]
 
* [https://habr.com/ru/company/pvs-studio/blog/348098/ Почему важно проверять, что вернула функция malloc]
Строка 168: Строка 146:
  
 
A: используйте assert для медленных проверок, если нужна производительность - используется release режим сборки, туда эти проверки не попадут
 
A: используйте assert для медленных проверок, если нужна производительность - используется release режим сборки, туда эти проверки не попадут
 
Q: а что делать с malloc, который вернет NULL, это же и в release может случиться
 
 
A: сейчас у нас в арсенале нет способа просто решить эту проблему. Можно, но сложно. Поэтому ставим assert, чтобы развивать привычку. В заданиях незачем аллоцировать Очень Много Памяти, поэтому проблема не слишком актуальная. "В жизни" будет использоваться не assert, а какой-то другой механизм.
 
  
 
'''2. Стиль кода совпадает во всем проекте.'''
 
'''2. Стиль кода совпадает во всем проекте.'''
Строка 211: Строка 185:
 
'''8. Использование сторонних функций только из стандартной библиотеки языка, если противное явно не оговорено в задании.'''
 
'''8. Использование сторонних функций только из стандартной библиотеки языка, если противное явно не оговорено в задании.'''
  
В частности, написание C/C++ кода без привязки к платформе Linux; отсутствие POSIX-специфичных функций и структур. Не смотря на то, что целевая платформа - Linux, привычка писать платформенно-специфичный или компиляторо-специфичный код может однажды сыграть злую шутку. Комитет по стандартизации стремится к тому, чтобы не было необходимости писать такой код, включая новые и новые платформенно-независимые вещи в стандарт языка. Если "совсем никак", то лучше обернуть специфичную функцию: сделать <code>size_t mygetline(char **lineptr, size_t *n, FILE *stream) {return getline(lineptr, n, stream);}</code>  
+
В частности, написание C++ кода без привязки к платформе Linux; отсутствие POSIX-специфичных функций и структур. Не смотря на то, что целевая платформа - Linux, привычка писать платформенно-специфичный или компиляторо-специфичный код может однажды сыграть злую шутку. Комитет по стандартизации стремится к тому, чтобы не было необходимости писать такой код, включая новые и новые платформенно-независимые вещи в стандарт языка. Если "совсем никак", то лучше обернуть специфичную функцию: сделать <code>size_t mygetline(char **lineptr, size_t *n, FILE *stream) {return getline(lineptr, n, stream);}</code>  
  
 
'''9. Не использовать exit где-либо, кроме main.cpp'''
 
'''9. Не использовать exit где-либо, кроме main.cpp'''
 
Представьте, что вы пользуетесь какой-то библиотекой. Позвали функцию из нее, а ей что-то не понравилось (не хватило памяти, запись на диск не удалась, ...) и она позвала в своих недрах exit. Это полная катастрофа, потому что на этом работа программы завершается, а в месте вызова вы об этом даже не узнаете. Это может быть очень болезненно: может быть, в оперативной памяти хранится результат длительных вычислений, или ценная информация, это могли быть данные, за которые нужно платить (обращения к платным сервисам) и много что еще. Главное - за вас кто-то другой решил, как программа должна себя вести. Никогда не используйте exit в библиотеках.
 
Представьте, что вы пользуетесь какой-то библиотекой. Позвали функцию из нее, а ей что-то не понравилось (не хватило памяти, запись на диск не удалась, ...) и она позвала в своих недрах exit. Это полная катастрофа, потому что на этом работа программы завершается, а в месте вызова вы об этом даже не узнаете. Это может быть очень болезненно: может быть, в оперативной памяти хранится результат длительных вычислений, или ценная информация, это могли быть данные, за которые нужно платить (обращения к платным сервисам) и много что еще. Главное - за вас кто-то другой решил, как программа должна себя вести. Никогда не используйте exit в библиотеках.
 
Во всех заданиях прослеживается структура: сделать некоторый набор действий в рамках некоторой модели. Модель находится в одном месте, управляющая логика - в другом. Здесь проходит условная черта: модель - это библиотека, управляющая логика - конечный пользовательский код.
 
Во всех заданиях прослеживается структура: сделать некоторый набор действий в рамках некоторой модели. Модель находится в одном месте, управляющая логика - в другом. Здесь проходит условная черта: модель - это библиотека, управляющая логика - конечный пользовательский код.
Обработка ошибок должна осуществляться с помощью кодов возврата в языке Си и с помощью кодов возврата либо исключений в языке Си++.
+
Обработка ошибок должна осуществляться с помощью кодов возврата либо исключений.
В языке Си разрешается делать exit в том случае, если гарантированно освобождены все ресурсы (позваны free, fclose, ...). Например, разрешается в main обработать ошибки так: передать код возврата в специальную функцию, которая по коду возврата напечатает сообщение и позовет exit. Но делать так не рекомендуется, потому что эквивалентным образом можно позвать return из функции main: 
 
 
 
<code>
 
void exitOnError(int ec) {...; exit(ec);}
 
int main(){...; if (ec) {exitOnError(ec);}
 
 
 
vs
 
 
 
int printError(int ec){...; return ec;}
 
int main(){...; if(ec) {return printError(ec);}
 
</code>
 
  
 +
'''10. Пространства имен'''
 +
Не писать ничего в глобальное пространство имен в заголовочных файлах. И не использовать `using namespace` на неограниченный scope (например, в начале .cpp файла).
  
 
To be continued.
 
To be continued.

Версия 20:45, 15 января 2020

Осенний семестр

Лекции

Лектор: Суворов Егор Федорович (egor_suvorov@mail.ru)

Весенний семестр

Планы лекций Егора: github.com/yeputons/hse-2019-cpp

Прочие материалы

Ссылки:

Книги:

  • Язык C++
    • Б. Страуструп, Язык программирования С++
    • Б. Эккель, Философия C++
  • Дополнительно
    • Б. Страуструп, Дизайн и эволюция языка C++
    • С. Майерс, Эффективное использование С++/Эффективное использование STL
    • Г. Сеттер, Решение сложных задач на C++/Новые сложные задачи на C++
    • Р. Седжвик, Алгоритмы на C++

Осенний семестр

Презентации первого семестра Евгения Линского (I поток):

Планы лекций Егора (II поток): github.com/yeputons/hse-2019-cpp

Презентации ликбеза по С:

Оценка за курс

Лабораторные и домашние

Практика 1: Соколов

Преподаватель: Вячеслав Соколов (vi.soksok@gmail.com, +7 921 780 11 12)

Trac: sokolov

Письма просьба идентифицировать префиксом в теме [HSE][CPP]

Linux

  • Исследование бинарных артефактов
    • nm list symbols from object files
    • readelf displays information about ELF files
    • c++filt demangle symbols
Ограничение процесса по ресурсам в Linux
  • Рекомендуется использовать systemd-run: systemd-run --scope -p MemoryLimit=2M -p MemoryAccounting=yes ./a.out. В этом примере ограничивается память 2 мебибайтами.

Дальнеший текст - для тех, кто хочет более глубокого погружения.

Краткий перечень ключевых слов и какие есть проблемы при использовании:

  • cgroups - можно просто взять и использовать, но придется повозиться, не очень удобно. Требует понимания, как что устроено, соответственно и времени потребует.
  • ulimit - много разных версий, от конкретного shell-а зависит как пример использования, так и доступная функциональность. Легко может не сработать.
  • Использование LD_PRELOAD и собственного аллокатора памяти. У меня аллокатор libmemrestrict.so по ссылке из статьи крашится с SEGFAULT, возможно, это я где-то напортачил. Если захотите идти этим путем - спрашивайте меня, есть несколько нетрививальных моментов.
  • docker - популярный способ работы с контейнерами

Для тех, кому хочется поэкспериментировать с консолью

  • Эмуляторы терминала
    • terminator
    • tilix
    • а также много других, легко ищутся
  • Shell
    • bash по умолчанию, но есть кое-что поудобнее, например
    • fish - interactive shell, создан, чтобы быть удобным
    • zsh - для любителей писать скрипты

Системы сборки

Для наших нужд достаточно Make

Если проект становится большим, нужно что-то посложнее, например (альтернативы):

Полезные сайты

  • godbolt - compiler explorer (удобно делиться примерами)
  • ideone - возможность делиться кодом, поддержка разных ЯП, исполнение кода online
  • stackoverflow - огромное количество ответов на разные вопросы. Прежде чем самому задавать вопрос, будет полезно ознакомиться с текстом https://habr.com/ru/post/339038/
  • [1] - список библиотек из 1-2 файлов
  • [2] - список open-source библиотек на языке Си++
  • [3] - история языка Си++

Статьи

Практика

16.01.20


Требования корректности, предъявляемые к работам

1. Проверка контрактов функций.

На мой взгляд, чуть ли не единственной обязательной к использованию парадигмой и доступной во всех языках программирования, выше ассемблера, является [4]. Ключевые понятия: предусловия, постусловия и инварианты. В языках C/C++ соблюдение контрактов контролируется с помощью assert, возведения сигналов, бросания исключений.

Данный подход является обязательным, потому что гарантирует максимально раннее обнаружение проблемы. Игнорирование подхода приводит к проблемам в самых неожиданных местах. Программа может вести себя как корректная, хотя содержит в себе большое количество ошибок, пока очередная проблема не приведет к лавине. Примеры неожиданно всплывших проблем: https://software-testing.ru/library/testing/general-testing/2082-horrible-bugs

Требуется производить проверку контрактов:

  • предусловий: с помощью assert в начале функции. Пример: Указатель: может быть нулевым? Нет? assert.
  • инвариантов: по ходу выполнения функции. Пример: по ходу алгоритма нужно произвести удаление элемента из контейнера. Можно проверить, что этот элемент вообще присутствует в контейнере.
  • постусловий: В конце функции и с помощью тестов. Пример: в результате удаления вершины из списка она не должна быть достижима из головы списка. Это можно провалидировать с помощью assert.

Проверки контрактов упрощают восприятие кода и могут при определенном стечении обстоятельств заменять документацию.

Q: но ведь проверка контракта может быть медленной, теряем производительность

A: используйте assert для медленных проверок, если нужна производительность - используется release режим сборки, туда эти проверки не попадут

2. Стиль кода совпадает во всем проекте.

Либо везде табуляции для отступов, либо везде пробелы. Именование переменных либо везде snake_case, либо везде camelCase. Желательно отделять разные сущности друг от друга написанием. Например, можно выделять:

  • МАКРОСЫ()
  • КОНСТАНТЫ
  • функцииКоторыеЧтоТоДелают()
  • переменныеКоторыеЗачемТоПонадобились
  • ИменаКлассовИСтруктур

Допустимы конфликты написания (когда по имени нельзя однозначно восстановить, какая это языковая конструкция), но не стоит для всего использовать одно и то же написание.

3. Каждая строчка кода что-то делает.

Если строчку кода можно удалить без изменения поведения программы, значит, ее нужно удалить.

4. В языке Си использовать void(void), а не void().

5. Отсутствие утечек памяти, Undefined Behaviour и других проблем Ваша программа должна компилироваться и корректно завершаться, а соответствующая утилита не должна находить какие-либо проблемы:

  • в debug и release (-DNDEBUG) режимах сборки
  • будучи запущенной из-под gdb
  • будучи запущенной из-под valgrind
  • после компиляции с -fsanitize=address
  • после компиляции с -fsanitize=undefined
  • после компиляции с -fsanitize=leak
  • на разных версиях разных компиляторов (gcc, clang, msvc, ...)

включая комбинации этих опций, кроме случаев ошибок в самих утилитах и несоответствия компилятора Стандарту. (Да, и то, и то бывает.)

6. Именование должно быть понятным кому угодно, не только лишь Автору, но и всем. Имя функции должно отражать особенности ее поведения. Имя переменной должно быть говорящим. Лучше всего, если вне контекста можно понять, за что отвечает / что делает та или иная функция, структура, переменная. Стандарт языка в этом смысле не является примером для подражания.

Этот раздел не может быть формализовать и ложится целиком и полностью на здравый смысл. Здесь зачастую нет "идеального" решения, и если вам не удается достичь совершенства - не расстраивайтесь, таких как вы - легион. Проблема именования является одной из ключевых в программировании, вызывает большое количество дискуссий, требует много времени и внимания разработчиков.

7. Если была допущена какая-то проблема в реализации функции, должен быть написан тест, детектирующий эту проблему.

8. Использование сторонних функций только из стандартной библиотеки языка, если противное явно не оговорено в задании.

В частности, написание C++ кода без привязки к платформе Linux; отсутствие POSIX-специфичных функций и структур. Не смотря на то, что целевая платформа - Linux, привычка писать платформенно-специфичный или компиляторо-специфичный код может однажды сыграть злую шутку. Комитет по стандартизации стремится к тому, чтобы не было необходимости писать такой код, включая новые и новые платформенно-независимые вещи в стандарт языка. Если "совсем никак", то лучше обернуть специфичную функцию: сделать size_t mygetline(char **lineptr, size_t *n, FILE *stream) {return getline(lineptr, n, stream);}

9. Не использовать exit где-либо, кроме main.cpp Представьте, что вы пользуетесь какой-то библиотекой. Позвали функцию из нее, а ей что-то не понравилось (не хватило памяти, запись на диск не удалась, ...) и она позвала в своих недрах exit. Это полная катастрофа, потому что на этом работа программы завершается, а в месте вызова вы об этом даже не узнаете. Это может быть очень болезненно: может быть, в оперативной памяти хранится результат длительных вычислений, или ценная информация, это могли быть данные, за которые нужно платить (обращения к платным сервисам) и много что еще. Главное - за вас кто-то другой решил, как программа должна себя вести. Никогда не используйте exit в библиотеках. Во всех заданиях прослеживается структура: сделать некоторый набор действий в рамках некоторой модели. Модель находится в одном месте, управляющая логика - в другом. Здесь проходит условная черта: модель - это библиотека, управляющая логика - конечный пользовательский код. Обработка ошибок должна осуществляться с помощью кодов возврата либо исключений.

10. Пространства имен Не писать ничего в глобальное пространство имен в заголовочных файлах. И не использовать `using namespace` на неограниченный scope (например, в начале .cpp файла).

To be continued.

Практика 2: Свиридкин

Практика 3: Лапшин

Практика 4: Гулецкий