Немного о форматировании кода и другие советы

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

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

Общие советы

Все предупреждения компилятора должны быть устранены.

Следует во всех файлах проекта придерживаться одного стиля оформления кода.

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

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

Не нужно жалеть строки текстового документа. В первую очеред код должен быть читаем. Следовательно необходимо сделать всё для этого.

В коде должны отсутствовать магические числа. Их лучше заменить на константы, да-да именно на константы, а не на макросы.

Не рекомендуется использовать директивы препроцессора макросов функций. Для этого стоит использовать inline функции.

Следует всегда в конструкцию switch добавлять секцию default.

Если пишите код для встраиваемых систем, используйте типы данных из бибилотеки stdint.h, такие как uint8_t, int16_t и др. Это гарантирует правильную размерность переменных на разных платформах. Особенно заметно отличие в архитектуре ARM и AVR, где int имеет размерность в 32 и 16 бит соответсвенно.

В качестве проверки терминального нуля всегда следует использовать \0.

Оператор ?: всегда следует заключать в скобки. Как думаете чему будет равно следующее выражение?

delta = 10;
alfa = 5;
return ( delta - alfa ? 0 : 1 ) <= 0;

Результат будет true, а как подумали вы, не говорите, что false?

Помните о точках следования. Следующий код в разных режимах оптимизации может работать некорректно. Переменная result будет принимать значение 0xFA или 0xAF.

uint8_t arr[] = { 0x0F, 0x0A, 0x01, 0x0D };
uint8_t *pointer = arr;

uint8_t GetCell( void )
{
    return *( pointer++ );
}

...

uint16_t result = GetCell() << 4 | GetCell();

Функции и переменные глобальные только для одного конкретного модуля объявляйте как static.

Следует выравнивать однотипный код таблицей.

Очень полезным будет использовать статический анализатор кода, например PC-Lint или PVS Studio. Последний в некоторых случаях можно использовать бесплатно.

Не забывайте комментировать код. Это в первую очередь нужно именно вам.

Как следует называть переменные, функции и всё остальное

Имена должны быть по возможности короткими, но достаточно точно описывающими суть сущности.

Переменные

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

uint8_t next_object;   ///< правильно
uint8_t nextobject;    ///< неправильно

Константы и макросы

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

Функции

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

Имя функции должно быть глаголом.

uint16_t DATA_GetValue( DATA_ParamType_t type );
...

void LOAD_Init( void )
{
    ...
}

Типы данных

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

Перечисления

Имена элементов необходимо писать заглавными буквами.

Имя элемента начитается с имени самого перечисления или имени явно выражающего принадлежность к определенному перечислению.

typedef enum
{
     PACK_TELEMETRY
    ,PACK_LOADOFF
    ,PACK_PONG
    ,PACK_ERROR
} COMM_Pack_t;

Если внимательно смотрели, то заметили, что запятая на следующей строке - это влияние статьи. Теперь я делаю именно так.

Форматирование кода

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

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

uint32_t    akb_volt    = 0;
uint32_t    sun_volt    = 0;
uint32_t    sun_curr    = 0;

int32_t     dE          = 0;
uint16_t    dT_low      = 0;

int32_t     calc_buff   = 0;

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

Скобки ограничивающие блок кода должны находиться на отдельных строках с отступом соответсвующим оператору к которому они относятся.

Конструкции вида читаются намного сложнее:

if ( flag ){
   // work
}

if ( flag1 ){ /* work */ }

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

for ( uint8_t i = 0; i < 64; i++ )
{
    arr[ i ] = rand();
}

Можно сделать и так:

for ( uint8_t i = 0; i < 64; arr[ i++ ] = rand() );

Но такой код сложнее отлаживать, и сложнее наращивать функционал.

Волшебный смысл пробелов

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

4 - это наиболее оптимальное число пробелов с точки зрения восприятия (ИМХО). Встречал людей, которые ставили один пробел и код читался как одна большая простыня.

А теперь рассмотри пример где же необходимо ставить пробелы для улучшения читабельности кода.

uint8_t arr[ 16 ] = { 0 };

... // Где-то здесь заполнение массива

for ( uint8_t i = 0; i < 16; i++,  arr[ i ]++ )
{
    if ( arr[ i ] < max_value )
    {
        arr[ i ] = delta * arr[ i ] / k;

        CAN_Send( arr[ i ] );
        OUT_Blink();
    }
}

Где необходимо ставить пробелы:

  • внутри скобок (, ), [, ], {, };
  • после запятой;
  • вокруг арифметический знаков и знаков сравнения;
  • после операторов for, if, wfile.

Где пробелы совсем не нужны:

  • между именем функции и скобками с параметрами;
  • при объявлении массива если не указано количество элементов uint8_t arr[] = { 0x0F, 0x0A, 0x01, 0x0D };;
  • между скобками функции если она вызывается без параметров;
  • пред и пост инкремент/декремент должны быть написаны слитно с сущьностью к которой они применены;
  • перед запятыми и точками с запятой;
  • вокруг точки . и вокруг -> при обращении к полям.

Послесловие

Всё что написано это не руководство к действию, особенно в части форматирования. Но к "Общим советам" советую прислушаться.