А.В.Столяров. Оформление программного кода. Методическое пособие

image of the cover

Аннотация

В пособии изложены основные принципы, применяющиеся для повышения читаемости текстов компьютерных программ и их доступности для анализа человеком; в частности, даются рекомендации по разбиению программ на модули и подсистемы, уделяется много внимания различным стилям расстановки структурных отступов и незначащих (декоративных) пробелов.

Пособие ориентировано на студентов программистских специальностей, преподавателей, программистов.

Публикация в бумажном варианте

Второе издание опубликовано издательством МАКС Пресс (Москва) в 2019 году. ISBN 978-5-317-06257-6.



first edition coverПервое издание опубликовано издательством МАКС Пресс (Москва) в 2012 году по заказу издательского отдела ф-та ВМК МГУ. ISBN 978-5-317-04282-0.

Электронная версия

Электронная версия, идентичная печатному изданию, доступна здесь: http://www.stolyarov.info/books/pdf/codestyle2.pdf

Электронная версия первого издания доступна здесь: http://www.stolyarov.info/books/pdf/codestyle.pdf

Статус бумажной версии

В настоящее время может быть приобретена в здании факультета ВМК, а также заказана с доставкой на этом сайте.

Первое издание доступно в библиотеке ВМК МГУ.

Системы документирования кода

Здравствуйте!
Хотелось бы спросить: как Вы относитесь к системам документирования исходного кода вроде Doxygen? И является ли их использование желательным при оформлении исходных текстов? Не нашёл этого в книге. Приходилось сталкиваться с ними не только в C и C++, но и в GNU Octave, Python и golang. Лично мне они показались удобными, особенно Doxygen с поддержкой LaTeX для формул: и вычурных кодировок для греческих букв не надо, и ASCII-artа.

И хотелось бы поблагодарить за книгу, после её прочтения стало проще объяснять ряд вещей студентам, для которых программирование - не специальность, а инструмент.

admin аватар

Doxygen я активно

Doxygen я активно использую уже лет пятнадцать как, очень нравится. На вопрос о желательности этого я всё-таки предпочту, чтобы каждый отвечал для себя сам. Ну а в книжке этого нет, потому что тяжело объять необъятное.

Лично я думаю,

Лично я думаю, что если человеку Doxygen действительно нужен - то он легко разберётся по документации без детального разбора в учебнике. Но для начинающего программиста на Си само существование таких вещей совсем не очевидно. Именно как парадигмы, похожую на Literate Programming Кнута. Лично я наткнулся на Doxygen во многом случайно, уже через несколько лет работы с Си. Причём уже видел нечто подобное в MATLAB. Ни в университетском курсе Си для химиков, ни в наших отечественных учебниках о системах документирования кода не упоминалось.

Добрый

Добрый день!

При прочтении заметил несколько опечаток:

1) стр. 22, предпоследний абзац
"К примеру, если вы работаете над процедурой, длина которой 20-30, и видите возможность..."

Пропущено слово "строк" после цифр "20-30".

2) стр. 73, в примере описания функции int check на C, как делать не нужно, перепутаны местами 0 и 1, если сравнивать с кодом, который приведен ниже.

Не уверен, что следующие два пункта являются опечатками, если нет, то не могли бы вы пояснить, как читать и понимать данные типы возвращаемых значений?

3) стр. 79, static const char * const *

Правые const и * здесь не лишние?
Такое объявление компилируется, но "расшифровать" тип у меня не получается.

4) стр. 103, const char *GetNameById(int id) const

Аналогично предыдущему, правый const не лишний?

admin аватар

Спасибо :)

Опечатки (1) и (2) уже известны, но всё равно спасибо.

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

С пунктом 4 ещё проще -- там Си++, и это константный метод, то есть функция-член класса, которая гарантированно не изменяет объект, для которого она вызвана, и может быть, следовательно, использована для константных объектов (например, полученных по константной ссылке).

Вроде осознал

Спасибо!

Другими словами, это разматывание чем-то похоже на матрешку: указатель указывает (масло масляное) на все, что слева от него, и, если внутри левой части есть еще указатель, поступаем аналогичным образом, сказав, что указатель указывает на этот указатель, который далее указывает на что-то еще.

А в каких случаях оно применяется? Из названия функции в примере следует, что таким образом можно представить распарсенную командную строку.
Используется ли это, например, для задания массива возможных сообщений программы?
const char * const messages[] = {
"Message1",
"Message2",
};
Тут, конечно, и без const перед char поменять в памяти ничего не получится, но зато в указателях на подобный список все будет прописываться явно и компилятор ругнется, где нужно.

P.S. Хотя при попытке следующего присваивания
char * const *p = messages;
gcc выдает предупреждение о несовместимости типов указателей, а не о сбрасывании модификатора const. Можете объяснить, почему так происходит?

А четвертый пункт проще, да, спасибо.

admin аватар

кто на что похож

это разматывание чем-то похоже на матрешку

Можно и так сказать, хотя мне сложно судить, в правильном ли смысле вы привлекли эту аналогию.

Используется ли это, например, для

Да, почему бы и нет

Тут, конечно, и без const перед char поменять в памяти ничего не получится

Очень даже получится. Сами строки. Точнее, в итоге-то, конечно, не получится, но ошибка вылезет не при компиляции, а уже во время исполнения. Программа с грохотом превратится в тыкву.

gcc выдает предупреждение о несовместимости типов указателей, а не о сбрасывании модификатора const

Потому что вы меняете не тип самого указателя, а тип элемента, на который он указывает. Предупреждение "discards qualifiers" вылезет, если p описать как

const char **p = messages;

Можно и так

Можно и так сказать, хотя мне сложно судить, в правильном ли смысле вы привлекли эту аналогию.

Каждый указатель - матрешка, которая может содержать в себе другие матрешки. А область памяти, на которую указывает "последний" указатель, пусть будет той самой неразборной матрешкой.

Очень даже получится. Сами строки. Точнее, в итоге-то, конечно, не получится, но ошибка вылезет не при компиляции, а уже во время исполнения. Программа с грохотом превратится в тыкву.

Ну да, я и имел в виду Segmentation fault во время исполнения.

Потому что вы меняете не тип самого указателя, а тип элемента, на который он указывает.

Понял ошибку, спасибо.

admin аватар

Каждый

Каждый указатель - матрешка,

Ну нет, в таком виде, пожалуй, это всё-таки неправильно. Указатели-то имеют одинаковый размер, в отличие от матрёшек. И это как раз довольно важный момент: указатель не содержит весь список, он содержит только информацию о месте, где в памяти лежит первый элемент списка.

Тут скорее аналогия с ниточками какими-нибудь, которыми связаны элементы.

Матрешки

Указатели-то имеют одинаковый размер, в отличие от матрёшек.

Согласен, как раз в этом моменте аналогия и дает сбой, но на то она и аналогия :)

admin аватар

Тут вопрос в

Тут вопрос в том, для чего привлекается аналогия. Если для того, чтобы объяснить техническую сущность списка, то эта аналогия не позволит достичь поставленной цели.

Разрыв строки до бинарной операции или после

Добрый день!
Недавно обратил внимание, что в code convention PEP-8 рассуждается на тему, вставлять ли разрыв строки для длинных арифметических или логических выражений до знака бинарной операции или после (утверждается, что разрыв перед является более удобочитаемым): https://www.python.org/dev/peps/pep-0008/#should-a-line-break-before-or-...
В то время как в вашей книге, на странице 45, приводится пример с разрывом после знака операции. Хотелось бы узнать, вы все еще придерживаетесь такого же мнения? И если да, то почему?

admin аватар

В примере по

В примере по вашей ссылке имеется "отягчающее обстоятельство" — там часть операндов с плюсом, часть с минусом. Я бы так делать вообще не стал, но если делать — то да, знак лучше ставить непосредственно перед операндом.

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

Оформление отступов

Странно, а почему не был упомянут стиль Уайтсмитс?
Он выглядит, например, так:
C++

int main()
    {
    foo1();
    foo2();
    foo3();
    ...
    return 0;
    }


Как можно заметить, отступы делаются для скобок, но при этом внутри скобок отступов не делается.

admin аватар

Нечто подобное

Нечто подобное упоминается на стр. 39 в качестве примера недопустимого стиля. Отличается только тем, что открывающая скобка там оставлена на одной строчке с оператором, но здесь у вас ситуация другая — заголовок функции, а не оператора.

В общем, я не считаю этот стиль допустимым. Причина — на стр. 39 объяснена.

Настройки просмотра комментариев

Выберите нужный метод показа комментариев и нажмите "Сохранить установки".