1) сначала прототипы функций, потом main, и лишь потом определение собственных функций
2) Функция должна иметь осмысленное имя, делать что-то полезное и возвращать значений, т.е. избавьтесь от функций, к-е вызываются единожды внутри другой собственной функций и содержат 2-3 строки кода, выдернутого из контекта, например, print_header() - для 'шапки" таблицы)
3) программа ведет диалог с пользователем на русском языке (system ("chcp 1251") помогает всем)
4) приветствие пользователю обязательно и должно включать назначение программы
5) объявления переменных расположены в начале программы
6) ИЗБЕГАЙТЕ «МАГИЧЕСКИХ ЧИСЕЛ»
7) используйте функции только стандартной библиотеки языка Си (стандарт С99), все функции, имена которых включают заглавные буквы "чужие".
Подробное описание (Источник C Style and Coding Standards. Glenn Skinner. Suryakanta Shah Bill Shannon AT&T Information System Sun Microsystems). Пример промышленного CodeStyle см.
1. Правила именования файлов.
Используйте следующие расширения файлов:
- для файлов с исходном кодом на языке С - расширение .c;
- для перемещаемых объектных файлов - расширение .o;
- для заголовочных файлов - расширение .h;
- для архивных библиотечных файлов - расширение .a;
2. Организация программы.
Разделяйте программы на модули. Модульный код с четким разделением меньше подвержен программным ошибкам, проще сопровождается и легче расширяется. Хотя язык С и не представляет инструментария для разделения программы на модули, разделение на модули можно достичь с помощью методов и соглашений, описанных ниже.
Модуль – это группа процедур и данных, совместно реализующих некую абстракцию, например: организацию стека или задачу управления памятью и т.д.
Примечание: модуль состоит из интерфейсной части (в языке С роль «интерфейса» играют заголовочные файлы (файлы с расширением .h) с объявлениями функций, определениями константных значений и структур данных) и определений функций в файлах с исходным кодом (файлы с расширением .с) .
Старайтесь повсеместно использовать модульный код. Степень модульности определяется исходя из сложности проекта/задачи и производительности обмена между модулями. Пользуйтесь здравым смыслом при распределении задачи на модули и соблюдайте баланс. Для выполнения простых операций использовать макросы препроцессора.
3. Организация файлов.
Файл должен разделяться на секции. Секции в файле должны отделяться пустыми строками.
Хоть формально максимальные требования к размеру файла не предъявляются, не рекомендуется использовать более 300 строк кода в одном файле, так как код будет трудным для восприятия.
Так же следует избегать слишком длинных строк. Не все терминалы и текстовые редакторы хорошо работают с ними.
3.1 Заголовочные файлы.
Порядок разделов для .h файлов выглядит следующим образом:
а) Идентификатор автора: файл должен начинаться с указания автора кода.
б) #ifndef: Заголовочный файл часто может подключаться в нескольких файлах. Чтобы избежать многократного включения заголовочного файла в рамках одного проекта используется директива #ifndef. Директива #ifndef проверяет объявление идентификатора, который объявляется после директивы #ifndef. При первом подключении заголовочного файла данные файла используются, при последующих подключениях, директива #ifndef прервет использование данных заголовочного файла. Обычно идентификатор состоит из символа подчеркивания и имени файла. Директива #ifndef должна следовать сразу после указания автора.
в) Блок комментариев: после #ifndef должен идти блок комментариев, описывающий назначение объектов в файле (функций, глобальных переменных и т.д.). Описание должно быть коротким и по делу.
для глобальных переменных - продолжение фразы "используется для ...", для функций - глагол-действия (что делает функция)
г) #includes: После блока комментариев подключаются дополнительные .h файлы.
д) #defines: далее следуют определения с помощью директивы #defines, относящиеся к модулю в целом, но не к отдельным элементам структур.
е) typedefs: после определения идентификаторов с помощью #define, идут определения новых типов данных с помощью оператора typedef.
ж) Декларация структур: если определение директивы #define, относится к глобальным данным (например, флаги слова), то определение должно следовать сразу же после объявления этих данных.
з) Декларация функций: объявление функций должно идти после декларации структур. Все внешние функции должны быть объявлены, даже те, которые возвращают int или ничего не возвращают (void).
и) Объявление внешних переменных: далее идет декларация внешних переменных.
к) Не определяйте статические и глобальные переменные в заголовочных файлах. Декларация переменных в заголовочных файлах часто является показателем плохого распределения кода по файлам.
3.2 Файлы с исходным кодом.
Порядок разделов для .c файлов приведен ниже:
а) Идентификатор автора: файл должен начинаться с указания автора кода.
б) Блок комментариев: сразу после имени автора должен идти блок комментариев, описывающий содержимое файла (назначение или основные функции программного модуля).
в) #includes: далее должно идти подключение заголовочных файлов.
г) typedefs and #defines: после подключения заголовочных файлов должны следовать определения с помощью операторов typedef и #define.
д) Определение структур: далее идет определение структур.
е) Глобальные переменные: они должны располагаться после определения структур.
ж) Объявления функций: далее должна следовать декларация функций. Декларироваться должны только приватные функции. Публичные (библиотечные) функции уже объявлены в заголовочных файлах, которые подключены выше. Хотя С-компилятор и не требует объявления функций возвращающих int, декларировать такие функции считается хорошим тоном.
з) Определения самих собственных функций:. Каждой функции должен предшествовать блок комментариев. Функции должны следовать в определенном порядке. Лучше если они будут упорядочены сверху вниз, чем снизу вверх. Функции схожего уровня «абстракции» лучше располагать рядом. Функции желательно размещать как можно ближе к месту их вызова. При наличии большого числа функций практически не имеющих взаимосвязи можно располагать их в алфавитном порядке.
Пример описания функции
/*
* проверяет является ли список пустым
* plist - указывает на начало списка
* возращает 1, если список пуст, и 0 - в противном случае
* каждый элемент списка структурного типа List
*/
int list_is_empty(List* plist){
Обратите внимание!
назначение (что делает?)
описание передаваемых параметров
возвращаемое значение
побочные эффекты и другие необходимые пояснения
4. Табуляция.
а) Каждый новый уровень должен отделяться табуляцией.
б) Каждая строка должна быть ограничена размером страницы (код не должен переноситься на новую строку).
5. Комментарии.
Комментарии должны содержать информацию, относящуюся только к коду программы. Комментарии, вводящие в заблуждение, хуже, чем отсутствие комментариев. В комментариях должны быть отражены:
а) Должны быть хорошо описаны нетривиальные решения и неординарные ситуаций, такие как «побочный эффект». Следует избегать дублирования информации, возникающее при комментировании очевидного кода.
б) В комментариях должны быть отражены все изменения в функциях, внешних переменных и указателях.
в) При использовании оператора case следует вводить соответствующие комментарии.
г) Все объявления должны комментироваться, за исключением декларации одноразовых счетчиков (например, для организации цикла) и очевидных имен констант.
д) В комментариях не надо писать то, что не нужно для понимания кода.
5.1 Блочные комментарии.
Блочные комментарии используют для описания содержимого файла, описания назначения функций, структур данных и алгоритмов.
Блочные комментарии должны использоваться в начале каждого файла и перед каждой функцией.
Блок комментариев перед функцией должен описывать только ее функционирование и содержать описание входных параметров, алгоритма работы функции и возвращаемых функцией значений.
Пример блочного комментария:
/*
* Файл содержит реализацию стека
* на основе статического массива
*/
5.2 Однострочные комментарии.
Однострочные комментарии используются для описания неочевидных «условий» и «возвращаемых значений».
а) Однострочные комментарии должны иметь отступ равный отступу комментируемого кода. Текст комментария должен быть отделен от операторов /* и */ пробелом.
if (argc > 1) {
/* получение входного файла из командной строки */
if (freopen(argv[1], "r", stdin) == NULL)
perror("can’t open %s\n", argv[1]);
}
б) Блочный комментарий должен использоваться, когда комментарий не помещается в одну строку.
5.3 Комментарии, завершающие строку с кодом.
Данный вид комментариев наиболее часто используется.
а) Комментарий должен отделяться от конца строки с кодом достаточно далеко.
б) Все комментарии параметров и локальных переменных должны быть выравнены в один столбец.
в) Если в блоке кода более чем один комментарий, то коментарии должны быть выравнены в один столбец.
if (a == 2)
return (TRUE); /* special case */
else
return (isprime(a)); /* odd numbers only */
г) Не комментировать каждую строку кода!
6. Объявления.
а) Одно объявление в строке, если нужны комментарии к ней.
int level; /* число уровней */
int size; /* размер таблицы */
int lines; /* число строк таблицы */
предпочтительнее давать имена переменным, объясняющих их назначение :
int size, nrows, ncols;
б) Не смешивайте декларацию переменных и функций, как приведено в примере:
long dbaddr, get_dbaddr();
в) Не объявляйте локальные переменные с одним и тем же именем во внутренних блоках функции, например:
void func()
{
int cnt;
...
if (condition) {
register int cnt;
...
}
}
г) В структурах и объединениях (union) объявление каждой переменной должна начинаться с новой строки и сопровождаться комментарием. Открывающаяся фигурная скобка «{» должна быть на одной строке с объявлением структуры или объединения (union). Закрывающаяся фигурная скобка «}» должна быть на отдельной строке и располагаться в ее начале. Например:
struct boat {
int wllength; /* water line length in feet */
int type; /* see below */
long sarea; /* sail area in square feet */
};
/* defines for boat.type */
#define KETCH 1
#define YAWL 2
д) При декларации каждой функции необходимо явно указывать возвращаемое значение. Если функция не возвращает значений, то указывается – void.
е) Определения с помощью typedef используются для «параметризации» программ с целью повышения портабельности и модифицируемости. Используйте typedef для собственных структур, но не "закрывайте" указатели
typedef char *Mything1; /* Переопредление типа */
typedef label {
...
...
} *seclebp; /*ТАК НЕ ДЕЛАТЬ!*/
7. Операторы.
7.1 Простые операторы.
а) Каждая строка должна содержать не более одного оператора. Не следует делать, как указано в примере:
argv++; argc--;
7.2 Сложные операторы.
Сложные операторы – это операторы, которые содержат список операторов заключенных в фигурные скобки «{}».
а) Список в фигурных скобках должен отступать от оператора на один уровень (tab).
б) Открывающаяся фигурная скобка должна быть в конце строки начала сложного оператора, а закрывающаяся скобка должна быть на отдельно строке, в столбце, с которого начинается сложный оператор
Использование фигурных скобок для выделения одиночного оператора, если он является частью структуры операторов, таких как if-else или for, определяется индивидуально. Например:
if (condition) {
if (other_condition)
statement;
}
в) Операторы if, if-else, if-else if-else должны иметь следующую форму:
if (condition) {
statements;
}
if (condition) {
statements;
} else {
statements;
}
if (condition) {
statements;
} else if (condition) {
statements;
} else if (condition) {
statements;
}
г) Оператор for должны иметь следующую форму:
for (initialization; condition; update) {
statements;
}
В операторе for используйте не более трех переменных. Очень тяжело воспринять организацию цикла при использовании большого количества переменных. Если нужно используйте отдельные операторы условия в теле цикла.
д) Оператор while должен иметь следующую форму:
while (condition) {
statements;
}
е) Оператор do-while должен иметь следующую форму:
do {
statements;
} while (condition);
ж) Оператор switch должен иметь следующую форму:
switch (condition) {
case 1:
case 2:
statements;
break;
case 3:
statements;
break;
default:
statements;
break;
}
Каждый оператор switch должен иметь состояние «default». Последний break – избыточен, но его применение желательно. Это избавит от ошибок при добавлении в конец оператора switch нового case. В языке С использование оператора switch желательно минимизировать.
8. Пустые строки.
Пустые строки улучшают восприятие кода, объединяя разделы (участки кода) имеющие логическую взаимосвязь. Пустые строки нужно использовать в следующих случаях:
а) После секции #include.
б) После блока с #define – константами и #define – макросами.
в) Между декларациями структур.
г) Между функциями.
д) После декларации локальных переменных и между открывающейся фигурной скобкой функции и первой строкой функции с исходным кодом, если в этой строке не декларация локальных переменных.
9. Пробелы.
А) Пробелы должны использоваться:
После ключевых слов:
if, switch, case, for, do, while
В списках параметров после запятой:
pow(a, b)
int func(int a, int b);
Все бинарные операции, за исключением «.» и «->» должны отделяться от операнда пробелом.
a += c + d;
a = (a + b) / (c * d);
операторы в условии для цикла «for» должны разделяться пробелами:
for (expr1; expr2; expr3)
B) НЕ используйте пробелы
С Унарными (одноместными) операциями & * + - ~ ! sizeof ++ --, а также Явное указание типа не должно отделяться пробелом:
i++, &a, sizeof(struct file);
myfunc((unsigned)ptr, (char *)x);
внутри скобок
sizeof( struct file );
pow( a, b );
С) Объявляя данных типа указателя или функцию, которая возвращает тип указателя, лучше использовать * рядом с именем данных или именем функции, а не рядом с именем типа.
char *banner;
unsigned long long memparse(char *ptr, char **retptr);
char *match_strdup(substring_t *s);
10. Имена файлов, функций и переменных.
Договорённости об именовании делают исходный код программы более понятным и читабельным. Дают дополнительную информацию о функциях и идентификаторах, например, в имени констант может быть указан ее тип, что поможет в понимании кода. Ниже приведен ряд правил, которым необходимо следовать:
а) Имена переменных и функций должны быть короткими, но осмысленными. Однознаковые имена переменных желательно избегать, за исключением «одноразовых» переменных. Имена переменных i, j, К, М, N должны использоваться для целых чисел, с, D, Е для символьных переменных (строк), p - для указателей. Избегайте использовать имя переменной «L», так как в некоторых редакторах трудно отличить L от 1.
б) К именам указателей должен добавляться символ «p» для каждого уровня ссылки. Например, указатель на переменную db должен иметь имя ptrdb. А указатель на указатель db должен иметь имя pptrdb.
в) Разделяйте слова в длинных именах подчеркиванием, например: create_panel_item.
г) Имена констант, определенных директивой #define, должны состоять только из заглавных символов.
д) Название типов переопределенных с помощью typedef, должны заканчиваться на «_t» или начинаться с заглавной буквы, например:
typedef enum { FALSE, TRUE } bool_t;
typedef struct node node_t;
е) Имена «макросов» должны состоять только из заглавных букв, за исключением «макросов» действующих как функции. В последнем случае имя «макроса» должно состоять только из строчных букв.
ж) Имена переменных и структур должны содержать только строчные буквы.
Примечание: следует избегать имен переменных, которые отличаются только регистром, например: Foo и FOO.
з) Индивидуальные элементы enums должны быть уникальными. Уникальность имени переменной можно гарантировать префиксом содержащим имя функции или модуля, в котором они используются, например:
enum rainbow { rb_red, rb_orange, rb_green, rb_blue };