После просмотра кода коллег, с которыми создаю совместный проект, решил составить небольшой список рекомендаций с целью повышения качества нашего кода. Хотя написанное по большей части очевидно, не исключаю, что оно может пригодиться еще кому-то из моих двух - трех читателей.
- Придерживаться здравого смысла.
- Быть немного идеалистом, но помнить про пункт 1.
- Писать программы структурно, что в свою очередь означает:
- Для программирования в крупном:
- Код программ следует разбивать на относительно небольшие модули. Связи модулей друг с другом не должны быть циклическими.
- Код модулей разбивать на относительно небольшие функции с ограниченным количеством параметров.
- Программирование в малом:
- Не использовать goto и его частные случаи для циклов: break и continue;
- Использовать return только в конце тел функций, возвращающих значение.
- Не использовать функции, прерывающие нормальный поток исполнения, например exit(); Исключением является вещи, вроде стандартного макроса assert, предназначенного для выявления ошибок программирования во время исполнения. Его использование только поощряется, но требует осторожности.
- Использовать switch исключительно как частный случай многоветочного if (таблица 1 пример 5).
- Не использовать некоторые конструкции и приёмы, разработанные специально для этого:
- Тернарную операцию "expr ? exprTrue : exprFalse" . If - более читаемая и универсальная конструкция.
- Операцию присваивания в качестве подвыражения с побочным эффектом даже когда это позволяет сэкономить аж целую строку кода (табл.1 пример 1). Тоже самое касается инкремента ++ и декремента --.
- Операцию запятую, позволяющей записывать выражения из плохо связанных подвыражений.
- Умножение и деление на степень двойки с помощью побитовых операций. Понятность и переносимость страдает при том, что даже самый захудалый компилятор сам все правильно преобразовывает.
- Не надо считать, все в мире циклы, являются частными случаями сишного for. Использовать его исключительно для полного прохода в рамках целочисленных границ, известных до выполнения цикла. "for (i = low(a); i <= high(a); i += step);".
- Для обозначения нулевого указателя вместо 0 использовать макрос NULL из файла string.h
- Несмотря на то, что в С нет полноценного логического типа, следует разделять логические и целочисленные значения, а заодно и указатели (табл.1 пример 2).
- При объявлении не следует группировать произведённые от одного типа разнотипные переменные (табл.1 пример 3).
- Глобальные идентификаторы подбирать понятные и по возможности лаконичные. Межмодульные имена должны хранить в себе печать модуля.
- Различать массивы и указатели на единичные элементы, используемые как переменные параметры фунцкции (табл.1 пример 4).
- Использовать условную компиляцию с помощью инструкций препроцессора по минимуму (табл.1 пример 6). В случаях, когда используется код для разных веток условной компиляции, который не может быть собран в рамках одной сборки, следует выносить зависимости от условной компиляции за рамки основного кода в отдельные модули/единицы_компиляции.
- Уменьшить использование числовых типов, отличающихся друг от друга отсутствием знака или разрядностью. Из целых предпочитать int_least32_t или long, из чисел с плавающей точкой - double.
- Сообщения-предупреждения (warnings) от компилятора считать сообщениями об ошибках. Есть редкие исключения.
- Много всего, что я забыл либо поленился написать, то, что выходит за рамки коротких рекомендаций, а также то, о чём не ведаю. Эта часть, как водится, самая полезная.
Таблица 1. Примеры плохого и очень плохого
кодов
№
|
Очень
плохо
|
Плохо
(хорошо на Си/++ не бывает)
|
1
|
if
((i = get()) > 11) a();
|
i
= get();
if
(i > 11) a();
|
2
|
int
n = 1;
if
(n);
bool
b = true, a;
if
(b == true);
void
*p = &v;
if
(p);
if
(b) {/* капитан Очевидность
одобряет*/
a
= true;
}
else {
a
= false;
}
|
int
n = 1;
if
(n != 0);
bool
b = true, a;
if
(b);
void
*p = &v;
if
(p != NULL);
a = b; /* сестра таланта
*/
|
3
|
int a, *b,
c[34], d;
|
int
a, d; /* всё по полочкам */
int
*b;
int c[34];
|
4
|
void
func(char str[], char *сh) {
*(str
+ 6) = 'u';
ch[0]
= *str;
}
|
void
func(char str[], char *сh) {
str[6]
= 'u'; /* массиву - массивово */
*ch
= str[0]; /* указателю - указателево*/
}
|
5
|
switch
(s) {
case
1:
b();/* не
всегда ясно - забыт break
*/
case
2:/*или так задумано*/
c();
break;
сase
3:
if
(condition){
d();
break;
}
e();
}
|
switch
(s) {
case
1:
case
2: /* чтобы не играть в догадки*/
if
(s == 1) {/* всё должно быть явно */
b();
}
c();
break;
case
3:
if
(condition) {
d();
}
else {
e();
}/* не забываем явный */
break; /* хоть это и последний case */
}/* бог
экономии строк негодует */
|
6
|
#define
A
cond
= true;
...
#if
defined A
b(c());
#else
if
(cond_/*замаскированная
ошибка*/) {
c(b());
}
#endif
|
a
= true;
cond
= true;
...
if
(a) {/* бог экономии наносекунд
в шоке */
b(c());
}
else if (cond) {
c(b());
}
|
Комментариев нет:
Отправить комментарий