ПРАВИЛО #1 – ФИГУРНЫЕ СКОБКИ
Блок программы, идущий после ключевых слов if, else, switch, while, do и for следует всегда окружать фигурными скобками ({}), даже если он содержит только одиночные или пустые операторы.
// Не следует так делать…
if (timer.done)
// Одиночному оператору нужны скобки!
timer.control = TIMER_RESTART;
// А вот так правильно ...
while (!timer.done)
{
// Даже пустой оператор должен быть окружён скобками.
}
ПРАВИЛО #2 – Ключевое слово «const»
Ключевое слово const следует использовать:- для объявления переменных, которые не должны меняться после инициализации,
- для определения передаваемых по ссылке параметров функции, которые не могут быть изменены,
- для определения полей в структурах и объединениях, которые не могут быть изменены
- в качестве альтернативы директиве #define при определении числовых констант.
const unsigned char dirPort = 0xff;
Аргументация: Переменные объявленные с ключевым словом const будут защищены компилятором от непреднамеренных изменений.
ПРАВИЛО #3 – Ключевое слово "static"
Ключевое слово static следует использовать для описания всех функций и переменных, не используемых за пределами модуля, в котором они описаны.
static void InnerFunction(void)
{
….
}
Аргументация: Ключевое слово Си – static, имеет несколько значений. На уровне модуля глобальные переменные и функции, объявленные с ключевым словом static защищены от случайного доступа из других модулей. Это уменьшает связанность и способствует инкапсуляции.
ПРАВИЛО #4 – Ключевое слово "volatile"
Ключевое слово volatile нужно использовать везде, где уместно, включая:- Объявление глобальной переменной доступной любому обработчику прерываний,
- Объявление глобальной переменной доступной двум или более задачам,
- Объявление указателя к отображаемым в памяти периферийным регистрам ввода/вывода
volatile unsigned int timer;
Аргументация: Правильное использование спецификатора volatile устраняет целый класс трудных для определения ошибок. Это ключевое слово сообщает компилятору, что переменная может быть изменена в любой момент без какого-либо действия со стороны ближайшего кода и компилятор не должен выполнять на ней некоторые оптимизации. {1}
ПРАВИЛО #5 – Комментарии
Комментарии не должны быть вложенными и их не следует использовать для отключения блока программы, даже временно. Для временного отключения блока программы используйте директивы условной компиляции (например, #if 0 ... #endif).
// Так делать нельзя...
/*
a = a + 1;
/* comment */
b = b + 1;
*/
// Так правильно...
#if 0
a = a + 1;
/* comment */
b = b + 1;
#endif
Аргументация: Вложенные комментарии и закомментированный код могут позволить неожиданным отрывкам кода быть прокомпилированными.
(Первый пример, кстати, не будет компилироваться. Компилятор принимает за комментарий все, что находится между первыми символами /* и первыми встреченными символами */. Pashgan)
(Первый пример, кстати, не будет компилироваться. Компилятор принимает за комментарий все, что находится между первыми символами /* и первыми встреченными символами */. Pashgan)
ПРАВИЛО #6 – Тип данных с фиксированной разрядностью
Всякий раз, когда разрядность целочисленных типов, в битах или байтах, в программе существенна, следует использовать вместо char, short, int, long, или long long тип данных с фиксированной разрядностью. Знаковые и беззнаковые целочисленные типы с фиксированной разрядностью должны быть такими, как показано в таблице.
Стандарт для знаковых и беззнаковых целочисленных типов
с фиксированной разрядностью
с фиксированной разрядностью
Аргументация: Разрядность фундаментальных типов данных языка Си - char, short, int, long, и long long зависит от реализации (от конкретного компилятора и аппаратной платформы), а это приводит к проблемам переносимости программ. Стандарт 1999 года не решил эту проблему, но ввел одинаковые имена типов, показанных в таблице. Они заданы в новом заголовочном файле <stdint.h>. Эти имена надо использовать, даже если придется создать typedef`ы вручную.
// Так делать нельзя...ПРАВИЛО #7 – Поразрядные операторы
Ни один из поразрядных операторов (другими словами, &, |, ~, ^, <<, и >>) не должен использоваться для управления целочисленными данными со знаком.int8_t signed_data = -4;
signed_data >>= 1; // не обязательно -2
Аргументация: Си-стандарт не определяет основной формат данных со знаком и оставляет определение эффекта некоторых поразрядных операторов на усмотрение авторов компилятора.
ПРАВИЛО #8 – Целые числа со знаком и без
Целые числа со знаком не должны сочетаться с целыми числами без знаков в сравнениях или выражениях. Десятичные константы, не подразумевающие наличие знаков, должны быть описаны с 'u' на конце.
// Так делать нельзя...
uint8_t a = 6u;
int8_t b = -9;
if (a + b < 4)
{
// если бы -9 + 6 было -3 < 4 как ожидалось.
// была бы выполнена эта ветвь
}
else
{
//но поскольку -9 + 6 это (0x100 - 9) + 6 = 253
//то будет выполнена эта ветвь
}
Аргументация: Некоторые детали управления целочисленными данными со знаком зависят от реализации (то есть от от конкретного компилятора). Кроме того, в результате смешивания знаковых и беззнаковых данных могут возникать информационно-зависимые ошибки.
ПРАВИЛО #9 – Параметризированные макросы против inline функций
Не следует использовать параметризированный макрос, если для выполнения той же задачи может быть написана inline функция. {2}// Так делать нельзя...
#define MAX(A, B) ((A) > (B) ? (A) : (B))
// ... если вместо этого вы можете сделать так.
inline int Max(int a, int b)
Аргументация: С использованием директивы препроцессора #define ассоциируется множество рисков, и многие из них связаны с созданием параметризированного макроса. Систематическое применение круглых скобок (как показано в примере) важно, но это не устраняет вероятность двойного инкремента - MAX(i++, j++). Другие опасности неправильного использования макроса включают сравнение данных со знаком и без, или любой тест данных с плавающей запятой.
ПРАВИЛО #10 – Оператор-запятая
Оператор запятая (,) не должен использоваться внутри описания переменной.// Так делать нельзя…
char * x, y; // вы хотите, чтобы «y» был указателем, или нет?
Аргументация: Цена размещения каждого объявления в отдельной строке низкая, а риск, что компилятор неправильно «поймет» ваши намерения высокий.
СНОСКИ
1. Исходя из опыта консультирования множества компаний, я подозреваю, что подавляющее большинство встроенных систем содержит ошибки из-за нехватки ключевых слов volatile. Такие виды ошибок обычно обнаруживают себя как «глюки».2. Ключевое слово Си++ inline было добавлено в Си-стандарт в 1999-м году.
Michael Barr "Bug-Killing Coding Standard Rules for Embedded C". Вольный перевод ChipEnable.Ru
Comments
давно взял себе за правило всегда объявлять все переменные с фиксированной разрядностью (uint8_t, etc.)
"Веревка достаточной длины, чтобы выстрелить себе в ногу" Ален И. Голуб
Там есть все вышеперечисленн ое и даже больше. Обращайтесь к классике.
А то мы уже начали волноваться :)
Кто знает что есть такая книга :-) хорошо что есть такой сайт! Где можно узнать про книги и не только.
С правилом №5 не согласен - комментарий, что /* */, что // написать и удалить в процессе отлаки в разы быстрее и удобнее, чем #ifdef. При помощи #ifdef имеет смысл комментрировать (заодно и выделяя таким образом) наброски нереализованных кусков в процессе написания, но не отключаемый в процессе отладки код. Аналогично не согласен со многими тезисами у Голуба.
Для правила №6 есть дополнение - существуют еще типы uint_leastXX_t и uint_fastXX_t, про которые тоже неплохо бы написать. Например, счетчик цикла от 0 до 10 имеет смысл объявлять не int, а uint_fast8_t, который будет 8-битным беззнаковым для AVR и 32-битным беззнаковым для ARM.
если
Code:
/*
чтоб раскомментировать, добавь косую черту вначале кода
/*/
RSS feed for comments to this post