Главная    Разработка ПО    Стиль Си и стандарты программирования 

Стиль Си и стандарты программирования.

C Style and Coding Standards.

Glenn Skinner
Suryakanta Shah
Bill Shannon

AT&T Information System
Sun Microsystems

Вольный перевод: Поисов Дмитрий

Цель данного документа – описание стиля программирования на языке С, используемого в компаниях AT&T и Sun. Общий стиль программирования облегчает взаимодействия при разработке одной программы несколькими программистами. Использование единого стиля программирования для всего проекта улучшает читабельность кода и упрощает сопровождение проекта.

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

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

Содержание

1. Привила именования файлов.
2. Организация программы.
3. Организация файлов.
3.1. Заголовочные файлы.
3.2. Файлы с исходным кодом.
4. Табуляция.
5. Комментарии.
5.1. Блочные комментарии.
5.2. Однострочные комментарии.
5.3. Комментарии, завершающие строку с кодом.
6. Декларация.
7. Операторы.
7.1. Простые операторы.
7.2. Сложные операторы.
8. Отступы.
8.1 Пустые строки.
8.2 Пробелы.
9. Имена файлов, функций и переменных.
10. Практика программирования.
11. Разное.
Заключение.

1. Привила именования файлов.

В операционной системе Unix используются определенные расширения файлов, обрабатываемых компилятором языка С.

Используйте следующие расширения файлов:

- для файлов с исходном кодом на языке С - расширение .c;
- для файлов с исходном кодом на языке assembler - расширение .s;
- для перемещаемых объектных файлов - расширение .o;
- для заголовочных файлов - расширение .h;
- для архивных библиотечных файлов - расширение .a;
- для файлов с исходным кодом на скриптовом языке C shell - расширение .csh;
- для файлов с исходным кодом командной оболочки Unix (Bourne shell) - расширение .sh;
- для файлов с кодом генератора синтаксических анализаторов yacc - расширение .y;
- для файлов с кодом генератора лексических анализаторов lex - расширение .l;

2. Организация программы.

Разделяйте программы на модули. Модульный код с четким разделением меньше подвержен программным ошибкам, проще сопровождается и легче расширяется. Хотя язык С и не представляет инструментария для разделения программы на модули, разделение на модули можно достичь с помощью методов и соглашений, описанных ниже.

Модуль – это группа процедур и данных, совместно реализующих некую абстракцию, например: таблицу символов или задачу управления памятью и т.д.

Примечание: Процедуры и данные, используемые другими модулями, называются «public» (публичными). Процедуры и данные, доступные только внутри модуля, называются «private» (приватными).

Модуль состоит из двух файлов: «интерфейсный» файла и «применяемый» файл. «Интерфейсный» файл содержит публичную (public) декларацию. «Применяемый» файл – содержит приватную (private) декларацию и код процедур. Приватная декларация в «применяемых» файлах должна быть объявлена как статическая.

В языке С роль «интерфейсного» файла играют заголовочные файлы (файлы с расширением .h). Роль «применяемых» файлов – файлы с исходным кодом на языке С (файлы с расширением .с).

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

3. Организация файлов.

Файл должен разделяться на секции. Секции в файле должны отделяться пустыми строками.

Хоть формально максимальные требования к размеру файла не предъявляются, не рекомендуется использовать более 3000 строк кода в одном файле, так как код будет трудным для восприятия.

Так же следует избегать слишком длинных строк. Не все терминалы и текстовые редакторы хорошо работают с ними.

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, декларировать такие функции считается хорошим тоном.

з) Определения: Наконец пришла очередь самих функций. Каждой функции должен предшествовать блок комментариев. Функции должны следовать в определенном порядке. Лучше если они будут упорядочены сверху вниз, чем снизу вверх. Функции схожего уровня «абстракции» лучше располагать рядом. Функции желательно размещать как можно ближе к месту их вызова. При наличии большого числа функций практически не имеющих взаимосвязи можно располагать их в алфавитном порядке.

4. Табуляция.

а) Каждый новый уровень должен отделяться табуляцией (8 пробелов).

б) Каждая строка должна быть ограничена размером страницы (код не должен переноситься на новую строку).

в) Вставляйте табуляцию при объявлении переменных для выравнивания имен переменных. Это делает код более читабельным. Например:

	extern int 	y;
	register int	count;
	char 		**ptr_to_strpp;

г) Имена и параметры функций должны идти в колонку по одному на строке. Это облегчит поиск по файлу. Тип, возвращаемый функцией, должен размещаться на отдельной строке (int необходимо указывать явно). Определения параметров функции должны идти сразу после имени функции. Каждый параметры функции должен быть явно указан. Все параметры функции должны располагаться на одном уровне отступа. Открывающиеся и закрывающиеся фигурные скобки должны располагаться на отдельной строке на одном уровне отступа. Все локальные определения переменных и код функции должны иметь отступ хотя бы на один уровень. Декларация переменных должна иметь отступ от кода функции на одну пустую строку. Например:

	/*
	* This function finds the last element in the linked list
	* pointed to by nodep and return a pointer to it.
	*/

	Node *
	tail(nodep)
		Node *nodep;
	{
		register Node *np; 	/* current pointer advances to NULL */
		register Node *lp; 	/* last pointer follows np */

		np = lp = nodep;
		while ((np = np->next) != NULL)
			lp = np;
		return (lp);
	}

д) Выражение, не умещающиеся в одну строку, например, вызов функции с множеством аргументов, должно быть перенесено после последней запятой, в случае вызова функции (а не посередине объявления аргумента), или после последнего оператора, который помещается на строке. Новую строку не следует начинать с бинарного оператора. В случае вызова функции, перенесенный на новую строку аргумент, должен быть выравнен с первым символом аргумента следующего непосредственно после открывающейся круглой скобки функции. Например:

	if (long_logical_test_1 || long_logical_test_2 ||
	    long_logical_test_3) {
		statements;
	}

е) Привала отступа при использовании операторов «if», «for», «switch» и т.д. описаны ниже в разделе 6.2.

5. Комментарии.

Комментарии должны содержать информацию, относящуюся только к коду программы. Комментарии, вводящие в заблуждение, хуже, чем отсутствие комментариев. В комментариях должны быть отражены:

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

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

в) При использовании оператора case следует вводить соответствующие комментарии.

г) Все декларации должны комментироваться, за исключением декларации одноразовых счетчиков (например, для организации цикла) и очевидных имен констант. Комментарии при декларации переменных должны быть выровнены в один столбец.

д) Устаревшую информацию следует своевременно удалять из комментариев.

е) Блоки комментариев не должны выделяться специальными полями из символов (например, звездочек).

ж) Комментарии не должны содержать специальных символов, таких как «backspace».

з) В комментарии не должны включаться данные о том, как создан файл или в какой директории он должен располагаться.

и) Комментарии не должны содержать списки авторов и историю изменений кода.

Существует три стиля комментариев: блочный, однострочный, завершающий строку с кодом. Эти стили комментариев описаны ниже.

5.1 Блочные комментарии.

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

Блочные комментарии должны использоваться в начале каждого файла и перед каждой функцией.

Файл, содержащий функцию main() должен содержать в начале файла блочный комментарий описывающий работу всей программы. Во всех остальных файлах в комментариях должно описываться только функционирование содержимого файла.

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

В блочных комментариях оператор /* должен идти с первого символа строки, в один столбец с оператором */. Перед каждой новой строкой комментариев должен идти символ *, вторым символом в строке (первый символ – пробел). Это позволит поиском строки «.\*» найти все блочные комментарии в файле. В первой и последней строках блочного комментария не должно быть текста. В начале блочного комментария тестовая строка должна отделяться от символа * на один пробел, далее отступ текста может увеличиваться при необходимости.

Пример блочного комментария:

	/*
	* Here is a block comment.
	* The comment text should be spaced or tabbed over
	* and the opening slash-star and closing star-slash
	* should be alone on a line.
	*/

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

5.2 Однострочные комментарии.

Однострочные комментарии используются для описания неочевидных «условий» и «возвращаемых значений».

а) Однострочные комментарии должны иметь отступ равный отступу комментируемого кода. Текст комментария должен быть отделен от операторов /* и */ пробелом.

	if (argc > 1) {
		/* Get input file from command line. */
		if (freopen(argv[1], "r", stdin) == NULL)
			error("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; 	/* indentation level */
	int size; 	/* size of symbol table */
	int lines; 	/* lines read from input */

предпочтительнее декларации:

	int level, size, lines;

Однако последний стиль декларации допустим при декларации нескольких схожих по назначению переменных простого типа, такого как int или char.

б) Не смешивайте декларацию переменных и функций, как приведено в примере:

	long dbaddr, get_dbaddr(); 	/* WRONG */

в) Не объявляйте локальные переменные с одним и тем же именем во внутренних блоках функции, например:

	void
	func()
	{
		int cnt;
		...
		if (condition) {
			register int cnt; /* WRONG */
			...
		}
	}

Хотя это и допустимый в языке С код, но в нем легко запутаться.

г) В структурах и объединениях (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

д) Все внешние переменные, определенные в текущем модуле, но использование которых так же требуется в других модулях, должны декларироваться в отдельном заголовочном файле. Например, если в модуле «проверки» используется переменная aud_stat, определенная в заголовочном файле aud_ex.h, то другие модули, которым требуется данная переменная, должны подключать заголовочный файл aud_ex.h. Все модули, в которых используются внешние переменные, должны объявлять их в заголовочном файле с именем: modulename_ex.h

е) Переменные и функции, доступ к которым ограничен одним файлом, должны декларироваться как static. Это позволит четко знать, что функции и переменное – частные, используются только в одном файле. И так же исключает возможность конфликта имен переменных и функций в разных файлах.

ж) При декларации каждой функции необходимо явно указывать возвращаемое значение. Если функция не возвращает значений, то указывается – void.

з) Определения с помощью typedef используются для «параметризации» программ с целью повышения портабельности и модифицируемости. А так же для улучшения возможностей документирования исходного кода. Однако использование директивы typedef скрывает исходный тип декларации. Эта проблема особенно актуальна для сложных программ и программ для которых важна эффективность работы, а следовательно осведомленность разработчиков во всех деталях. Выбор возможности и объема использования директивы typedef остается за разработчиком. В примере ниже приведено неуместное использование директивы typedef:

	typedef char *Mything1;	 	/* These typedefs are inappropriately */
	typedef int Mything2; 		/* used in the code that follows. */
	int
	can_read(t1)
		Mything1 t1;
	{
		Mything2 t2;
		t2 = access(t1, R_OK); 		/* access() expects a (char *) */
		if (t2 == -1)			/* and returns an (int) */
			takeaction();
		return (t2); 			/* can_read() returns an (int) */
	}

и) Директива typedef не должна использоваться для определения указателей на структуру, так как полезно, а иногда необходимо однозначно и быстро понимать, что в коде используется указатель. В примере ниже показывается как не стоит делать:

	typedef label {
		...
		...
	} *seclebp; /*WRONG*/

7. Операторы.

7.1 Простые операторы.

а) Каждая строка должна содержать не более одного оператора. Не следует делать, как указано в примере:

	argv++; argc--; /* WRONG */

б) Не используйте оператор «запятая» для объединения нескольких операторов в одной строке, или с целью исключения фигурных скобок, как это показано в примере:

	if (err)
		fprintf(stderr, "error"), exit(1); /* WRONG */

г) Не группируйте операторы с троичным условным оператором «?:», как это показано в примере:

	num = cnt < tcnt ? (cnt < fcnt ? fcnt : cnt) :
	tcnt < bcnt ? tcnt : bcnt > fcnt ? fcnt : bcnt; /* WRONG */

7.2 Сложные операторы.

Сложные операторы – это операторы, которые содержат список операторов заключенных в фигурные скобки «{}».

а) Список в фигурных скобках должен отступать от оператора на один уровень (tab).

б) Открывающаяся фигурная скобка должна быть в конце строки начала сложного оператора, а закрывающаяся скобка должна быть на отдельно строке, в столбце, с которого начинается сложный оператор (смотри пример ниже)

в) Использование фигурных скобок для выделения одиночного оператора, если он является частью структуры операторов, таких как if-else или for, определяется индивидуально. Например:

	if (condition) {
		if (other_condition)
			statement;
	}

Однако в рамках одного проекта необходимо использовать только один стиль.

Не используйте фигурные скобки, если тело циклов for или while пусто:

	while (*p++ != c)
		;

д) Операторы 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 должен иметь следующую форму:

	while (condition) {
		statements;
	}

do { statements; } while (condition);

и) Оператор switch должен иметь следующую форму:

	switch (condition) {
	case ABC:
	case DEF:
		statements;
		break;
	case XYZ:
		statements;
		break;
	default:
		statements;
		break;
	}

Каждый оператор switch должен иметь состояние «default». Последний break – избыточен, но его применение желательно. Это избавит от ошибок при добавлении в конец оператора switch нового case. В языке С использование оператора switch желательно минимизировать.

Каждый многострочный блок оператора switch отделяйте пустой строкой, как показано в примере:

	switch (condition) {
	case ABC:
	case DEF:
		statement1;
		.
		.
		statementn;
		break;

	case XYZ:
		statement1;
		.
		.
		statementm;
		break;

	default:
		statements;
		break;
	}

8. Отступы.

8.1 Пустые строки.

Пустые строки улучшают восприятие кода, объединяя разделы (участки кода) имеющие логическую взаимосвязь. Пустые строки нужно использовать в следующих случаях:

а) После секции #include.
б) После блока с #define – константами и #define – макросами.
в) Между декларациями структур.
г) Между функциями.
д) После декларации локальных переменных и между открывающейся фигурной скобкой функции и первой строкой функции с исходным кодом, если в этой строке не декларация локальных переменных.

8.2 Пробелы.

Пробелы должны использоваться:

а) В ключевых операторах (sizeof, return и т.д.) после открывающейся скобки должен стоять один пробел. Пробел не должен использоваться между именем функции и открывающейся скобкой.

	strcmp(x, "done"); /* no space between strcmp and ’(’ */
	if (cond) /* space between if and ’(’ */

б) Пробелы должны ставиться в списках аргументов после запятой.

в) Все бинарные операции, за исключением «.» и «->» должны отделяться от операнда пробелом. Другими словами, пробелы должны ставиться до и после операторов присваивания, арифметических операторов, логических операторов. Пробелы никогда не должны отделять операнды от унарных операторов, таких как адрес (&), указатель (*), инкремент (++), декремент (--) т.д.. Например:

	a += c + d;
	a = (a + b) / (c * d);
	strp->field = str.fl - ((x & MASK) >> DISP);
	while (*d++ = *s++)
		n++;

г) операторы в условии для цикла «for» должны разделяться пробелами:

	for (expr1; expr2; expr3)

д) Явное указание типа не должно отделяться пробелом, за исключением вызываемых функций, чьи возвращаемые значения игнорируются:

	(void) myfunc((unsigned)ptr, (char *)x);

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

9. Имена файлов, функций и переменных.

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

а) Имена переменных и функций должны быть короткими, но осмысленными. Однознаковые имена переменных желательно избегать, за исключением «одноразовых» переменных. Имена переменных i, j, К, М, N должны использоваться для целых чисел, с, Д, Е для символьных переменных (строк), p - для указателей. Избегайте использовать имя переменной «L», так как в некоторых редакторах трудно отличить L от 1.

б) К именам указателей должен добавляться символ «p» для каждого уровня ссылки. Например, указатель на переменную dbaddr должен иметь имя dbaddrp. А указатель на указатель dbaddrp должен иметь имя dbaddrpp.

в) Разделяйте слова в длинных именах подчеркиванием, например: create_panel_item.

г) Имена констант, определенных директивой #define, должны состоять только из заглавных символов.

д) Название типов переопределенных с помощью typedef, должны заканчиваться на «_t» или начинаться с заглавной буквы, например:

	typedef enum { FALSE, TRUE } bool_t;
	typedef struct node node_t;

или

	typedef enum { FALSE, TRUE } Bool;
	typedef struct node Node;

е) Имена «макросов» должны состоять только из заглавных букв, за исключением «макросов» действующих как функции. В последнем случае имя «макроса» должно состоять только из строчных букв.

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

Примечание: следует избегать имен переменных, которые отличаются только регистром, например: Foo и FOO.

з) Индивидуальные элементы enums должны быть уникальными. Уникальность имени переменной можно гарантировать префиксом содержащим имя функции или модуля, в котором они используются, например:

	enum rainbow { rb_red, rb_orange, rb_green, rb_blue };

и) Все внешние имена должны иметь префикс, идентифицирующий модуль, связанный с данной переменной (функций и т.д.), например:

	int nfs_stat;	/* variable belongs in nfs module */
	int vm_stat;	/* variable belongs to vm module */

10. Практика программирования.

а) Числовые константы не должны использоваться непосредственно в коде (в виде числа).

б) Определенные с помощью #define константы должны иметь осмысленные имена. Это также упростит корректировку больших программ, так как значение констант может изменяться путем изменения только значения указанного с помощью #define.

в) В ситуации, когда переменная может принимать ограниченное количество дискретных значений, предпочтительно использование типа данных enum, так как дополнительная проверка сможет осуществляться через lint.

г) Не рекомендуется использовать goto. Основная ситуация, когда допустимо использования goto – это выход из оператора switch.

д) Не используйте goto для перехода во внутрь блока программы, например так:

	goto label; /* WRONG */
	...
	for (...) {
		...
		label: 	statement;
		...
	}

е) Не присваивайте значения переменным в длинной строке декларации переменных, например:

	int a, b, c = 4, d, e; /* This is NOT a good style */

ж) Старайтесь не присваивать одно и то же значение нескольким переменным в одном операторе. Не используйте одновременно несколько присвоений, так как это тяжело воспринимается, особенно для сложных выражений. Например:

	foo_bar.fb_name.fchar = bar_foo.fb_name.lchar = ’c’;

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

и) Не переопределяйте составные части структур с помощью #define, вместо этого используйте union. Однако, директива #define может использоваться для определения ссылок на составные части union. Например, вместо:

	struct proc {
		...
		int p_lock;
		...
	};
	#define p_label p_lock

используйте:

	struct proc {
		...
		union {
		int p_lock;
		int p_label;
		} p_un;
		...
	};
	#define p_lock p_un.p_lock
	#define p_label p_un.p_label

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

	#ifdef vax
		...
	#elif sun
		...
	#elif u3b2
		...
	#else
		print unknown machine type;
	#endif /* machine type */

л) Не используйте логическое отрицание (!) в «нелогических» выражениях. В частности никогда не используйте логическое отрицание для проверки на нулевой указатель или проверки успешного завершения работы функции strcmp. Например:

	char *p;
	...
	if (!p) 				/* WRONG */
		return;
	if (!strcmp(*argv, "-a")) 		/* WRONG */
		aflag++;

м) Не используйте оператор присвоения в месте, где его легко спутать с оператором проверки на равенство. Например:

	if (x = y)
		statement;

Трудно понять, тольи программист хотел произвести присвоения, то ли ошибся и хотел сравнить переменные. Вместо этого используйте:

	if ((x = y) != 0)
		statement;

н) Не переопределяйте синтаксис языка С с помощью макрозамен/ #define BEGIN { Это сделает программу непонятной для всех, кроме автора замены, например:

	#define BEGIN {

11. Разное.

Постарайтесь, чтобы структура вашей программы совпадала с вашими целями:

а) Заменяйте

	if (boolean_expression)
		return (TRUE);
	else
		return (FALSE);

на

	return (boolean_expression);

б) Аналогично:

	if (condition)
		return (x);
	return (y);

более понятно, если записано так:

	if (condition)
		return (x);
	else
		return (y);

в) Проверка на ненулевое значение:

	if (f() != FAIL)

лучше, чем:

	if (f())

даже если FAIL может иметь значение 0. Это поможет Вам позже, когда кто-нибудь решит, что возврат по ошибки -1 лучше, чем 0. Исключение можно сделать для функций, удовлетворяющих следующим ограничениям:

- не имеющих цель вернуть значения отличное от true или false,
- возвращающих 0 для false и 1 для true,
- названных так, что однозначно понято, что возвращено будет true, например is_valid or valid, а не check_valid.

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

	a = b + c;
	d = a + r;

не стоит заменять на:

	d = (a = b + c) + r;

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

д) Если код содержит бинарные операции перед оператором «?» они должны помещаться в скобки:

	(x >= 0) ? x : ?x

е) Используйте lint для поиска неявных багов.

Заключение.

Указанный набор стандартов был приведён для стиля программирования на языке С++. Один из самых важных моментов – это использование пробелов и комментариев, делающее текст программы значительно лучше для восприятия.

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



Яндекс.Метрика

Рейтинг@Mail.ru