Перейти до основного вмісту

Стиль кодування

Кожна компанія має власний стиль кодування.
Мій стиль кодування походить зі стандартів, що застосовуються в автомобільній галузі, — удосконалених для embedded-розробки, — бо, на мій погляд, він найчитабельніший і найпідтримуваніший.
Нижче наведено обґрунтування кожного правила та те, як воно сприяє ясності й довгостроковій підтримуваності.


🔡 Змінні​

Змінні мають іменуватися в стилі lower camelCase.
Ім'я змінної має містити щонайменше три символи.

❌ Неправильно​

int x, y, z;
for(int i = 0; i < 10; i++) { ... }

✅ Правильно​

int loopIndex;
float temperatureCelsius;
uint8_t sensorCount;

📘 Чому: Кожна змінна повинна мати змістовне ім'я.
Короткі, незрозумілі імена руйнують читабельність і ускладнюють підтримку коду.


⚠️ Зовнішні змінні​

Існування змінних extern має бути суворо заборонене.
Якщо таку змінну знайдено, її автора слід публічно лінчувати (образно кажучи 😉).

💡 Причина:
Зовнішні змінні майже неможливо відстежити — важко зрозуміти, хто їх записує чи читає.
Завжди інкапсулюйте дані за getters і setters.


🧱 Типи даних​

Імена типів дотримуються шаблону lowerCamelCase, але мають закінчуватися на _t,
за стандартним іменуванням у <stdint.h> (наприклад, uint8_t, uint16_t тощо).

✅ Приклад​

typedef uint16_t temperature_t;
typedef float voltage_t;
typedef uint8_t humidity_t;

🧩 Причина:
Ви одразу знатимете, чи ідентифікатор позначає тип, чи змінну.


🚫 Уникайте універсальних вбудованих типів​

Не використовуйте безпосередньо у проєкті звичайні uint8_t, uint16_t тощо.
Натомість визначайте власні семантично змістовні typedef.

✅ Це запобігає змішуванню несумісних змінних:
ви не зможете випадково присвоїти temperature_t змінній humidity_t.

temperature_t temp = 25;
humidity_t humidity = 40;
temp = humidity; // ❌ Compilation error

Це забезпечує безпеку типів і скорочує час налагодження.


🧭 Імена функцій​

Імена функцій мають використовувати стиль UpperCamelCase.
Кожна функція повинна містити префікс модуля, щоб було зрозуміло, кому вона належить.

✅ Приклад​

rcc_InitSystemClock();
rcc_GetClockFrequency();
gpio_SetPinState(GPIOA, PIN_5, true);

📘 Чому:
Одразу зрозуміло, чи ідентифікатор є змінною, функцією або типом.
Префікс модуля дає змогу легко знайти функцію в ієрархії проєкту.


⚙️ Директиви препроцесора​

Директиви препроцесора та макроси мають використовувати UPPER_CASE_WITH_UNDERSCORES.

✅ Приклад​

#define MAX_SENSOR_COUNT 8
#define ENABLE_DEBUG_MODE 1

📘 Чому:
Іменування верхнім регістром одразу показує, що це символ часу компіляції, а не змінна часу виконання.


📄 Імена файлів​

Імена файлів використовують стиль UpperCamelCase.
Кожне ім'я файлу має починатися з назви модуля, після якої йдуть підкреслення та опис функціональності.

✅ Приклад​

Rcc_Config.c
Gpio_Handler.c
Modbus_Transport.c

🧩 Повна структура організації файлів описана на окремій сторінці вікі:
Організація файлів


🗂️ Переліки (Enumerations)​

Типи переліків дотримуються UpperCamelCase для типу та UPPER_CASE для елементів переліку.

✅ Приклад​

typedef enum
{
STATE_IDLE,
STATE_RUNNING,
STATE_ERROR
} systemState_t;

📘 Чому:
Значення верхнім регістром відрізняють константи переліку від змінних, а суфікс _t чітко позначає тип.


🧮 Константи​

Константи, визначені в коді (не макроси), мають використовувати імена в стилі UpperCamelCase.

✅ Приклад​

const uint32_t SystemTimeoutMs = 5000;
const float PiValue = 3.14159f;

💡 Віддавайте перевагу const перед #define скрізь, де це можливо, — це забезпечує перевірку типів і контроль області видимості.


🧰 Структури та об'єднання​

Імена структур використовують UpperCamelCase,
а їхні поля — lowerCamelCase.

✅ Приклад​

typedef struct
{
uint8_t deviceId;
uint16_t firmwareVersion;
float batteryVoltage;
} DeviceInfo_t;

📘 Чому:
Послідовний регістр чітко розрізняє тип структури та її поля.


🔣 Макроси та inline-функції​

  • Макроси (#define) → UPPER_CASE_WITH_UNDERSCORES
  • Допоміжні inline-функції → UpperCamelCase (так само, як звичайні функції)

✅ Приклад​

#define ENABLE_INTERRUPTS() __enable_irq()
#define DISABLE_INTERRUPTS() __disable_irq()

static inline void DelayMs(uint32_t ms) { ... }

💬 Коментарі та документація​

Використовуйте коментарі у стилі Doxygen для всіх публічних функцій, типів і макросів.

✅ Приклад​

/**
* @brief Initializes system clocks.
* @param None
* @retval None
*/
void Rcc_InitSystemClock(void);

💡 Тримайте коментарі короткими, точними й написаними англійською.
Описуйте, чому щось робиться, а не що робиться — це має бути зрозуміло з самого коду.


🧾 Правила форматування​

ПравилоОпис
Відступи4 пробіли, ніколи табуляції
ДужкиСтиль K&R ({ в тому самому рядку)
Максимальна довжина рядка120 символів
ПробілиЗавжди пробіл після ком і навколо операторів
Порожні рядкиВикористовуйте для візуального розділення логічних секцій
Include guardsФормат #ifndef MODULE_FILENAME_H
Порядок include<system> → "project" → "module"

✅ Приклад​

#include <stdint.h>
#include "Rcc_Config.h"
#include "Gpio_Handler.h"

void Gpio_Init(void)
{
gpioState_t state = GPIO_LOW;
if (state == GPIO_LOW)
{
gpio_SetPinState(GPIOA, PIN_5, true);
}
}

🧩 Простори імен і префікси​

Кожен модуль повинен мати унікальний префікс (наприклад, rcc_, gpio_, adc_).
Це гарантує, що імена функцій і типів не конфліктуватимуть у межах системи.

📘 Практичне правило:

  • Префікс = назва модуля
  • CamelCase = функція
  • _t = тип
  • lowerCamelCase = змінна

Приклад узгодженості:

rcc_Init()
rcc_Config_t
rccState_t
rcc_GetClock()

✅ Підсумок​

ЕлементСтильПриклад
ЗміннаlowerCamelCaseloopIndex, sensorCount
ТипlowerCamelCase + _ttemperature_t, systemState_t
ФункціяUpperCamelCaseRcc_InitSystemClock()
Макрос / DefineUPPER_CASE_WITH_UNDERSCORESMAX_BUFFER_SIZE
КонстантаUpperCamelCasePiValue
Ім'я структуриUpperCamelCaseDeviceInfo_t
Поле структуриlowerCamelCasedeviceId
Значення enumUPPER_CASESTATE_IDLE
Ім'я файлуUpperCamelCase + підкресленняGpio_Handler.c