Секреты профессионального программирования

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

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

Как ни странно, мало кто рассказывает об этом тем, кто учится. Более того, очень часто в книгах, в Интернете и в университетах продвигаются совершенно разные концепции, которые могут быть даже вредными, сосредоточены на нерелевантных элементах и ​​пугать степенью сложности.

Как распознать хорошего программиста?

Как бы вы оценили навыки разработчика, написавшего приведенный ниже код:

Многие из вас наверняка подумают: «Хороший программист должен с этим легко справиться, я ничего из этого кода не понимаю». Когда вы говорите что-то подобное, вы, вероятно, имеете в виду несколько элементов, которые сразу бросаются в глаза. Автор знает, какие операторы в выражении вычисляются раньше, а какие позже. Следовательно, он может увеличивать (++) и получать значение из-под указателя (*) за один раз, сравнивать значения и присваивать (> i =) и т.д. Может использовать указатели на функции, прямое приведение адреса, сокращенный условный оператор. Работает с расширенными структурами данных — множественными вложенными структурами, двойными указателями. Оптимизирует выполнение программы за счет использования битовых сдвигов вместо обычного умножения на 13.

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

Из этого можно понять, что хороший программист:

  • выполняет как можно больше операций в одной инструкции для экономии места, и для этого использует расширенные языковые функции
  • знает, какова последовательность действий и каково поведение компилятора по умолчанию, поэтому ограничивает ненужные выражения, которые в любом случае будут выполняться по умолчанию
  • знает, какие операции повышают эффективность, снижают потребление ресурсов и улучшают производительность программы

Правда в том, что автор кода может хорошо знать язык, но программист из него ужасный. Почему? Именно потому, что читатель ничего в коде не понимает. Это делает совершенно бесполезной его работу.

Никто другой не сможет понять написанного. Сложно что-то добавить или изменить. Многие операции выполняются одновременно, поэтому отладить сложно. В том же коде есть ссылки на высокоуровневые функции (get_pid) и аппаратные регистры (TIM3-> CR) и даже на определенные области памяти.

Требования к программному обеспечению

Теперь рассмотрим, каким условиям должен соответствовать готовый продукт. Перед запуском проекта у нас должен быть список допущений, которые должны быть обязательно выполнены. Они могут включать поддержку некоторых периферийных устройств, настраиваемых пользователем, или предоставление временного интервала для сигналов управления.

Понятный код также должен обеспечивать удобство использования.

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

Читать также:  Введение в алгоритмы поиска в C++

От вышеперечисленных требований зависит, будет ли наш проект успешным. Данные требования позволяют легко проверить готовность проекта. Т.к. они известны заранее, это нам позволит оценить, что еще предстоит сделать, и четко укажет на цель, к которой мы стремились. Существует также второй набор требований, который мы назовем внутренними требованиями. Они касаются не конечного продукта, а самого процесса его производства. Они включают такие аспекты, как эффективный поиск и исправление ошибок, добавление новых функций, перенос и совместная работа множества людей над одним проектом.

Внешние требования:

  • обеспечение заданной функциональности
  • обеспечение комфорта использования (неявные требования)
  • своевременное завершение работы

Внутренние требования:

  • возможность быстро находить и исправлять ошибки
  • легко добавлять новые функции
  • адаптация кода к новым проектам и другим конфигурациям оборудования
  • сотрудничество многих людей по одной программе
  • простой для понимания код после длительного перерыва в разработке

Обратите внимание, что в приведенном выше списке нет ни слова об оптимизации, минимизации потребления ресурсов или скорости выполнения функций. Так что весь этот «академический подход» совершенно не соответствует действительности!

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

Специфика языка C

Философия языка C предполагает, что программист — компетентный человек — он всегда знает, что делает, и осознает последствия своих действий. Исходя из этого, полученные программы выполняются быстро, и возможны низкоуровневые операции с аппаратным обеспечением и адресами прямой памяти. Другим важным преимуществом является относительно простая реализация компиляторов, благодаря которой можно писать код на C для многих различных платформ.

К сожалению, есть и обратная сторона медали. Человек не непогрешим, и даже опытные программисты делают ошибки. Эти ошибки особенно опасны, потому что ошибка часто приводит к синтаксически правильным выражениям, которые не сразу замечаются (стандартный пример — = вместо == или & вместо &&). Это может привести к ошибкам, которые проявятся в другом месте кода. В результате их поиск может быть особенно проблематичным. Особого внимания заслуживают следующие аспекты:

  • Не все ошибки улавливаются компилятором. Возможно, вам будет полезно включить дополнительные предупреждения компилятора. Флаг Wall включает стандартный и расширенный набор предупреждений соответственно.
  • То, как был написан код, как с точки зрения используемого синтаксиса, так и с точки зрения выбора имен переменных, пробелов, выравнивания и т.д., влияет на удобочитаемость и простоту совершения ошибок.
  • Нечитаемым код, трудно понять, особенно если выражение длинное, основано на операциях по умолчанию, содержит много похожих операторов и имен и т.д. Здесь стоит отметить, что даже если такое сложное выражение является правильным, на его понимание мы потратим дополнительное время.
  • Плохо сформулированная фраза может содержать опасность или побочные эффекты.
  • Иногда компиляторы работают не так, как мы хотели. Некоторые ошибки являются результатом не случайной ошибки, а неправильного толкования. Особенно это актуально при использовании менее популярных конструкций.

Читать также:  Алгоритмы в STL

Самое важное правило, позволяющее избежать ошибок синтаксиса языка: всегда четко понимать, что вы имеете в виду!

Имена переменных и функций должны объяснять, для чего они нужны. Если вы используете переменную с измерением расстояния, напишите, что это измерение расстояния, а не sensor5_data. Если вы выходите из цикла, когда переменная равна 0, пишите while (var! = 0), а не while (var). Если вы выполняете несколько операций, разбейте их на последовательные строки. Если вы чувствуете, что сам по себе код переводится недостаточно хорошо, не бойтесь добавлять комментарии. Вы даже можете написать несколько строк. Лучше потерять на это минуту, чтобы потом не тратить на отладку несколько дней.

Как использовать наши природные способности

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

Просматривая код, мы часто не вчитываемся в него внимательно, и делаем вывод о нем на основе неполной информации и контекста.

Следовательно, пространственное расположение текста ускоряет поиск и понимание информации. Каждый из нас подсознательно пытается использовать его, и у него есть свои любимые способы вставки скобок и пробелов, именования функций, разделения программы на файлы и т.д. Со временем мы привыкаем к этому стилю и находим его простым в использовании. Если не дай бог использовать чужой код, то он для нас будет как-будто написан на китайском языке. Чтобы понять его, нам нужно будет очень сильно сосредоточиться, но все равно мало, что поймем. По этой причине очень важно иметь четко определенный стиль в каждом проекте и придерживаться его.

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

Поэтому важно, чтобы функции не были слишком длинными. Благодаря этому у нас будет меньше прокрутки. Операции, которые не важны с точки зрения данной проблемы, следует спрятать во вспомогательные функции.

Благодаря этому нам не нужно анализировать расширенные 3-строчные if со сложной последовательностью операторов, вместо этого у нас есть простое выражение:

Читать также:  Двусвязный список структуры данных в C++

Если у нас есть функция, которая работает с измерением аналогового датчика, нам не нужно продираться через код, поддерживающий АЦП. Вместо этого достаточно функции:

Отдельные выражения внутри функции стоит сгруппировать по задачам по 5-10 строк каждое и добавить к ним коллективный комментарий. Тогда читатель будет знать, что какой-то этап закончился и что часть информации из кратковременной памяти больше не понадобится. Значит не будет ощущения перегруженности информацией.

Человек имеет ограниченную способность оперировать сложными структурами, содержащими множество зависимостей. Код, в свою очередь, имеет практически неограниченные возможности для расширения сложности, связанной с количеством переменных и функций, уровнями вложенности и зависимостей между отдельными модулями. Поэтому очень важно ограничить эту сложность. По этой причине модули следует разделять на слои. Модуль более высокого уровня может использовать элементы более низкого уровня с четко определенным списком глобальных функций. Объем переменных должен быть ограничен, предпочтительно одним файлом. И программисту не нужно будет помнить, что эта переменная была изменена в этом файле.

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

Единый стандарт кода

Учитывая все аспекты, которые мы обсудили, стандарты кодирования используются в проектах профессионального программирования. Они устанавливают единый стандарт для именования файлов, функций и переменных, способа использования скобок и пробелов и т.д. Это упрощает добавление новых элементов по мере роста кода, и многие люди, добавляющие изменения одновременно, могут сотрудничать вместе, не создавая бардак в коде. Эти типы стандартов также часто поднимают вопрос о запрещенных языковых конструкциях и т.д.

Вывод

Информация, которую мы здесь собрали, является результатом нашего собственного опыта, а также знаний, найденных в книгах и в Интернете. Они, конечно же, могут быть субъективными или слишком философскими, но мы надеемся, что они окажутся полезными. Вдохновением для написания статьи послужила разница между тем, что мы узнали вначале, и тем, что оказалось действительно важным на практике.

Тому, кто собирается серьезно заниматься программированием, придется тоже это пережить, к этому стоит подготовиться заранее.

Хороший программист не хвастается тем, сколько сложных выражений или хитрых приемов он использует. Хороший программист умеет вовремя достигать поставленных целей, избегая при этом потенциальных подводных камней. Кроме того, он может быстро находить ошибки, а его код может использоваться другими людьми без каких-либо проблем. Чтобы стать хорошим программистом, нужно работать над своим стилем, анализировать код, делать выводы и искать элементы, которые можно улучшить.

С Уважением, МониторБанк

Добавить комментарий