Оформление программ    

  Правила ~ 
/Студентам/Оформление программ/Правила

Как НУЖНО оформлять
исходные тексты программ

Есть много разных дисциплин, в которых на практических/лабораторных занятиях студентам нужно написать ту или иную программу на том или ином языке программирования. И делают они это, согласно нашим наблюдениям, по принципу "абыкакнитьработало", совершенно игнорируя элементарные правила оформления текстов своих программ. На замечания преподавателей, как правило, не реагируют, особенно на младших курсах. К пятому курсу умнеют, начинают понимать. Впрочем, долой эмоции. Итак, тексты программ. Исходный текст программы - это, по-большому счету, такой же текст, как стихотворение, рассказ, web-страничка. Посмотрите на этот же абзац, только слегка покореженный:

          Есть    много разных дисциплин   , в кот
          орых на 
	практических/лабораторных заня
	                                       тиях

студен
там нужно напис
ать ту или
                                иную програ
                                --мму на том или 
ином языке                    программирования        .
           И делают они это   ,
	        согласно нашим наблюдениям, по пр
	        инципу " 
		                      аб
		      ы какнить раб
		      отало",
совершенно игнорируя элемент
           арные правила офо
                         рмления текстов св
                         оих програ
			 мм. 
На замечания пре
под авателей      ,  как правило, не     реаг
ируют ,особе
		нно 
на младших                      курсах.К пят
ому курсу ум
неют,нач
инают понимать.Впр
о чем, дол_ой эмоции. Итак,тексты
программ.
             Исходный            текст про
	     граммыэто      ,по.
	большому счету,такойже текст,как стихо
	   тво
	   рение,р
	   ассказ, web   -
	страничка. Посмотрите на это
	т же абзац, только
			слегка п
			окоре
		женный:

Не правда ли, читать и ПОНИМАТЬ такое не очень удобно и приятно? Ну так вот нам, преподавателям, тоже очень неудобно читать и ПОНИМАТЬ Ваши программы, написанные в стиле "как руки на клаву легли", особенно с экрана, и особенно-особенно-особенно, когда Вы, отчаявшись найти ошибку, из-за которой Ваша программа не работает, обращаетесь за помощью к нам. "Ой, а посмотрите, почему у меня тут эта не работает?". Бывает такое? Бывает. А я вот скажу, что отчасти причиной ошибки может быть как раз то, что программа совершенно безобразно отформатирована и Вы просто НЕ ВИДИТЕ дурацкого ляпсуса типа "скобочка не там стоит".

Так вот, Уважаемые студенты! Если Вы хотите слегка облегчить жизнь себе и преподавателям, то Вы ДОЛЖНЫ форматировать исходные тексты Ваших программ так, как изложено ниже. А преподавателям я настоятельно советую ОТКАЗЫВАТЬСЯ от поисков ошибок в программах, если они не удовлетворяют нижеприводимым требованиям.

В скобочках отмечу, что по поводу стилей форматирования исходных текстов программ существует немало всякоразных мнений и предпочтений. Приводимые ниже правила, по существу, являются правилами, принятыми для исходных текстов операционной системы Linux, зачастую представляя собой почти дословный перевод документа /usr/src/linux/Documentation/CodingStyle, написанного автором этой ОС. Однако, Ваш уважаемый слуга, то бишь я, имея кой-какой опыт длительного беспрерывного молочения по клаве с целью писания программ на языке Си, в итоге пришел примерно к таким же правилам и с тех пор, как я к ним пришел, мне почти всегда удавалось избегать "тупежа" над "волосатым" кодом. Так что внимайте. :)

Итак, начнем. Прежде всего, разрешите напомнить, что стандартный размер экрана - 80x25 символов (знакомест). Это не чья-то прихоть, именно такие размеры наиболее комфортны для восприятия. Все, наверняка, натыкались на web-странички (phpBB, о да... ), для чтения которых надо чуть не голову поворачивать слева направо при чтении строки (и Википедия туда же, к слову). 80 символов в строке максимум, если больше, читать неудобно. К сведению: в печатном деле есть правило, согласно которому в строке должно быть не более 66-ти (вроде бы) символов. Теперь приступим непосредственно к правилам оформления исходных текстов программ.

А не, не начнём... не могу не процитировать все любят node.js:

"
Следует ограничивать строки 80-ю символами. Да, экраны за последние несколько лет стали больше, но ваш мозг не стал.

"

А вот теперь погнали:

Правило #1,
про количество операторов на одной строчке

Количество операторов на строчке должно быть равно 1.

НЕправильно:

a = b; do while (0); printf("dah-bah-dee\n"); 

Правильно:


a = b; 
do while (0); 
printf("dah-bah-dee\n"); 

Правило #2,
про горизонтальные отступы

Все операторы, входящие в составной оператор (напоминаю, составной оператор - это все, что между begin и end в языке программирования Паскаль и между { } в языке программирования C) должны быть сдвинуты вправо. Напоминаю, есть такая клавиша на клавиатуре, TAB, вот ей и двигайте, не надо лепить пробелы. Кроме того, размер отступа должен быть 8 (ВОСЕМЬ) символов. Настройте редактор, если он меньше.

НЕправильно:


if ( a < b ) {
j = m;
i = l;
for (k = 0; k < N; k++) {
c[k] += (m-l);
u[k-j] = 2*j;
if ( l > a ) {
break;
};
};
printf("ooo, yeahhhh!!!!\n");
};

Правильно:


if ( a < b ) {

	j = m;
	i = l;
	
	for (k = 0; k < N; k++) {
	
		c[k] += (m-l);
		u[k-j] = 2*j;
		
		if ( l > a ) {
			break;
		};
	};
	
	printf("ooo, yeahhhh!!!!\n");
};

В том, что размер отступа ДОЛЖЕН БЫТЬ 8 СИМВОЛОВ, есть простой смысл и практическая польза. Именно, если текст у Вас начинает залезать вправо за границу экрана (за границу НОРМАЛЬНОГО экрана шириной 80 символов, а не 180!!!), то, стало быть, у Вас слишком велика вложенность блоков. Терпимая вложенность - от силы 3. Если она у Вас больше, это означает, что Вы плохо продумали алгоритм и структуру программы и Вам надо это дело переосмыслить, ввести какие-то функции, заменив какие-то большие части кода их вызовами, переделать алгоритм и т.п. А укажет Вам на это уехавший далеко вправо текст, если у Вас табуляция такая, какая должна быть. А должна она быть, напоминаю, 8 символов.

Правило #3,
про операторные скобочки

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

НЕправильно:


for (;;) {
	printf("тра-ля-ля\n");
	}; 

for j:=0 to N do begin
	WriteLn('тра-ля-ля');
	end;

Правильно:


for (;;) {
	printf("тра-ля-ля\n");
}; 

for j:=0 to N do begin
	WriteLn('тра-ля-ля');
end;

Допускается такой вариант:


for (;;) 
{
	printf("тра-ля-ля\n");
}; 

for j:=0 to N do 
begin
	WriteLn('тра-ля-ля');
end;

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

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

НЕправильно:


int my_favorite_function(void) {
	return 1;
}

function MyFavoriteFunction:boolean; begin
	MyFavoriteFunction := TRUE;
end;

Правильно:


int my_favorite_function(void)
{
	return 1;
}	

function MyFavoriteFunction:boolean;
begin
	MyFavoriteFunction := TRUE;
end;

Правила 1-2-3 соблюдаются почти АВТОМАТИЧЕСКИ, если набирать текст программы не так, как это делает человек, 2 дня назад впервые увидевший клавиатуру, а так, как сейчас будет сказано. Начинающие обычно набирают текст последовательно, и поэтому (ИМЕННО ПОЭТОМУ!!!)

Для того, чтобы избежать трат времени на исправление последствий такой забывчивости, текст программы нужно набирать НЕпоследовательно. К этому располагает сама грамматика и синтаксис почти любого языка программирования. Всегда есть ПАРНЫЕ символы, скобочки там всевозможные, составные операторы опять же. Если написали begin, то почему бы сразу не написать end? Если набрали открывающую скобочку, то почему бы сразу не набрать закрывающую, а потом не вернуться назад? Сейчас на примере распишу, как надо набирать.

for (i = 0; i < N; i++) {
	/* тело цикла */
}

Вот цикл. Набираем так: набрали "for", пробел, нажали левый Shift, набрали "(", сразу (!!!) за ней ")", Shift отпустили, пробел, опять нажали Shift, набрали "{" и опять сразу (!!!) "}", она ведь тут же, рядом на клавиатуре, теперь стрелочка назад и Enter. Если так набирать, то Вы просто не сможете нарушить правило #3. Далее: возвращаемся внутрь круглых скобочек и только теперь начинаем думать, а по какой же переменной мы хотели сделать цикл. Ставим два раза точку с запятой (можно и раньше было поставить, когда скобочки ставили) и вписываем чего надо. Дальше - End, Enter, TAB (правило #2!!!) и начинаем думать, что же мы хотели написать в теле цикла. Если наберете таким образом несколько конструкций типа for, while, repeat, if и прочая и прочая, то Вы будете потом недоумевать, а как еще-то можно набирать??? В противном случае Вы так и будете писать программы, которые невозможно читать, так и будете безнадежно путаться в скобочках и т.д. и т.п. ...

Кто щас сказал про всемогущие IDE/sublime/etc, в которых шаблоны для условий/циклов/etc "сами собой набираются"? vi/pico/nano/mcedit тебе в руки, лентяй.

Правило #4,
про горизонтальные пробелы

В конце строки пробелов быть не должно!!! Ну незачем там они. Пробелы ОБЯЗАТЕЛЬНЫ после запятой, как в обычном тексте. Пробелы полезны вокруг знака какой-нибудь операции.

НЕправильно:


for(k=NUM;k;k--){
	int tmp;
	tmp=some_func(k,arg2,arg3);
	printf("%d\n",tmp);
}

Правильно:

for (k = NUM; k; k--) {
	int tmp;
	tmp = some_func(k, arg2, arg3);
	printf("%d\n", tmp);
}

Правило #5,
про вертикальные пробелы (пустые строки)

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

НЕправильно:


int func_1(void)
{ return 1;
}int func_1(void)
{ return 2;
}
int main(void) {
LIST *l = get_head();double a, b;printf("Hello.\n");
for (l;l->next;l=l->next)
printf("%s\n", l->name);
};printf("Bye.\n");}

Правильно:


int func_1(void)
{
	return 1;
}

int func_1(void)
{
	return 2;
}

int main(void) 
{
	LIST *l = get_head();
	double a, b;

	printf("Hello.\n");

	for (l; l->next; l = l->next)
		printf("%s\n", l->name);

	printf("Bye.\n");
}

Правило #6,
про объявления переменных

Тип у переменной писать для КАЖДОЙ из них. На одной строчке - одна переменная.

НЕправильно:


int a,b,c;
float *q,w,u,z,tmp,ttt,qqq,aaa,bbb;
........
a,b,c,d,ttt,test,tt0: integer;
q,w,u,z,ppp,ddd,ddd0,ddd23: real;

Правильно:


int a;
int b;
int c;
float *q;
........
a: integer;
b: integer;
c: integer;
q: real;

Это правило вызывает наибольшее недоумение. Однако, его соблюдение делает текст более ясным и, кроме того, в процессе разработки при возникновении необходимости удалить описание какой-либо переменной это можно сделать одним нажатием (команда удалить строку), а не многократным нажатием Del или Backspace. Впрочем, если, к примеру, в программе есть двойной цикл for(), то управляющие переменные цикла можно описать на одной строчке:


int j,k;
int counter;

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

Правило #7,
про говорящие идентификаторы

Идентификаторы (названия переменных, типов, подпрограмм) должны быть значимыми настолько, чтобы читающий текст программы мог понимать смысл всех переменных и т.д. без присутствия рядом автора. Читающим может оказаться и сам автор ... где-то так через полгодика. И будет мучительно вспоминать, что же означало "ffff_1" и "aaa". Если есть какая-то производная, назовите ее Deriv, Proizvod, но никак не FF.

Правило #8,
про инициализацию массивов

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

НЕправильно:

double matrix[3][3];
....
matrix[0][0] = 1.5;
matrix[0][1] = 2.5;
......

Matrix : array[1..3, 1..3] of extended;
......
Matrix[1,1] := 1.5;
Matrix[1,2] := 2.5;

Правильно:


double matrix[3][3] = {
			{1.5, 2.5, ... },
			{...},
			{...},
		      };

const
	Matrix: array[1..3, 1..3] of extended = 
			(
				(1.5, 2.5, ...),
				(...),
				(...)
			);

Правило #9,
про функции/процедуры

9.1) Функции ДОЛЖНЫ БЫТЬ. Программа без подпрограмм - почти заведомо плохая программа, за исключением программы, выводящей сообщение "Hello, world!" (и то не всегда), см. пример в конце.
9.2) У функций должны быть параметры. Функция, использующая глобальные переменные - почти наверняка плохая, ибо ситуации, когда внутри функции необходимо использовать глобальную переменную, крайне редки. Подпрограмма, реализующая, например, метод Гаусса для решения систем линейных уравнений и не имеющая параметров - это УЖАСНО. Это как сотовый телефон, с которого можно звонить только на один навсегда заданный номер и на который можно звонить тоже только с одного номера (причем с таксофона).
9.3) Функции должны быть короткими, 2 экрана (50 строк) максимум. Это касается и главной функции программы, main() в языке C или главных begin end. в языке Pascal. Единственное оправдание длинной функции - это наличие в ней оператора множественного выбора с большим числом альтернатив.
9.4) Функции должны выполнять РОВНО ОДНО ДЕЙСТВИЕ. Например, подпрограмма, выполняющая решение системы линейных уравнений, НЕ ДОЛЖНА заниматься считыванием исходных данных, она должна получить их в виде аргументов. Она также не должна выводить результаты куда бы то ни было. Это должна делать другая подпрограмма.

Правило #10,
про ошибки и еще кое про что

Если та или иная функция может потенциально завершиться неудачно, это ОБЯЗАТЕЛЬНО следует проверять. Это предохранит Вас и преподавателя(!) от долгих минут тупого созерцания экрана в попытке понять, почему Ваше творение падает на ровном месте либо ведет себя в высшей степени загадочно.

НЕправильно:

char * buf;
buf = (char*)malloc(/*много-много байт*/);
buf[0] = some_value;

Правильно:

char * buf;
buf = (char*)malloc(/*много-много байт*/);
if ( !buf ) {
	perror("нет памяти");
	exit(1);
};
buf[0] = some_value;

Если возникшая ошибка такова, что программа/подпрограмма далее выполняться не может, условие проверки на ошибку ЛУЧШЕ писать так, чтобы оно было ИСТИННЫМ, если ошибка ПРОИЗОШЛА, а не наоборот и код строить так, чтобы избегать оборота else в операторе if.

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

НЕправильно:

char * buf;
buf = (char*)malloc(/*много-много байт*/);
if ( buf ) {
	/* здесь страница кода */
} else {
	perror("нет памяти");
};

Этот else может быть далеко от if, что уменьшает ясность программы.

Правильно:

char * buf;
buf = (char*)malloc(/*много-много байт*/);
if ( !buf ) {
	perror("нет памяти");
	/* выходим */
	exit(BAD_EXIT_CODE) 
	/* или return BAD_RET_VAL; */
};
/* в этом месте все хорошо, продолжаем */

Правило #11,
про данные

Организация данных в программе - это вопрос не менее важный (а иногда, возможно, даже более важный), чем алгоритмическая структурированность программы. Специально для целей структуризации данных существуют замечательные типы данных, структуры (или записи в терминах языка программирования Паскаль) и классы (в ООП-языках). Если Ваша программа работает с какой-либо сущностью, сия сущность, особенно если она сложная, ДОЛЖНА быть описана в программе посредством структуры или класса, если Вы пишете на каком-то объектно-ориентированном языке. Сложная программа, в которой нет ни одного массива или структуры - это ПЛОХАЯ программа.

В заключение приведу пример простенькой программы (вычисление определенного интеграла методом прямоугольников) в двух вариантах. Первый будет написан с нарушением (насколько это у меня получится) всех указанных выше требований, а второй - с соблюдением оных (опять же, насколько получится :-) ). Обе программы работают и дают одинаковый результат. Однако на первую смотреть не хочется (как я такую гадость смог написать, не понимаю ... чего не сделаешь ради студентов ...). Аналогия: Вам нужно иметь машину, чтобы ездить из дома на работу и обратно. Денег хватает хоть на Порше. Дорога хорошая. Результат (попадание на работу и домой) будет как-то достигнут независимо от того, купили ли Вы подержанную "копейку" (сиденья без обивки, краска облуплена, резина голая и т.д.... но ведь ЕЗДИТ же!!!) или нулёвую Вольво 840. Однако же (при наличии деньгоф) все почему-то предпочитают что-нить ближе к второму варианту, не правда ли? А я вот предпочитаю, чтобы программа не только работала, но еще и выглядела должным образом.

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

НЕправильно:

Правильно:

Почувствуйте разницу, что называется. Надеюсь, ТЫ понял. Или поняла. А кто будет говорить что-нибудь вроде "а зачем такие навороты", "это ж писать дольше" и т.п., тот модифицирует данную программу таким образом, чтобы метод интегрирования задавался в виде указателя на функцию в структуре INTEGR_TASK. Ладушки?

См. также тут

Дата последней модификации: 2018-04-29


/Студентам/Оформление программ/Правила

Содержимое данного сайта может быть использовано кем угодно, когда угодно, как угодно и для каких угодно целей. Автор сайта не несёт абсолютно никакой ответственности за землетрясения, наводнения, финансовые кризисы, глобальные потепления/похолодания, разбитые тарелки, зуд/онемение в левой/правой пятке читателя, эпидемии/пандемии свинячьего/птичьего/тараканьего и иных гриппов, а также за прочие негативные, равно как и позитивные, последствия, вызванные прямым или косвенным использованием материалов данного сайта кем бы то ни было, включая самого автора. При копировании/цитировании материалов данного сайта любым технически возможным в настоящее время способом, а также способом, могущим стать возможным в будущем, указание (либо неуказание) ссылки на первоисточник лежит, блин, тяжким грузом на совести копирующего/цитирующего.

Valid HTML 4.0 Strict Valid CSS!