Sources.RU Magazine Поиск по журналу
 

TopList

Оформление кода в C

Автор: Мяут

Вы считаете, что пересматривать свой код через долгие годы Вам не придется? Вы ошибаетесь. Очень часто программисты используют повторно код, написанный давно, и при внесении в него изменений или отладке могут появиться неудобства. В этой саттье я рассмотрю способы оформления кода. Благодаря единому и грамотному оформлению, комментированию участков кода, а также при работе в команде благодаря использованию "корпоративного стиля" Вы не столкнетесь с сильными трудностями. Я - заядлый программист на C/C++ и поэтому все примеры будут на этом языке, но надеюсь, что моя статья будет пролезна более широкому кругу читатетелей.

В C любая обособленная часть кода должна выделяться фигурными скобками ( { и } ); в Pascal эту функцию выполняют операторы begin и end соответственно. Однако, их можно устанавливать по-разному. Вот четыре основных стиля их расстановки:


1TBS

Этот стиль был впервые использован Кернинганом и Ричи в своей книге "The C Progamming Language". Расшифровывается как One True Bracing Style (единственный правильный стиль расстановки скобок). Иногда его называют K&R (Kernighan and Ritchie) или kernel стилем.

Вот пример кода (обычно используется восемь пробелов):

void f1(int i1) {
	if(i1==0) {
		 printf("Hello, World");
	 }
}

Плюс такого стиля - экономия вертикального пространства, однако иногда в скобках путаешься, или тяжело найти ее в конце длинной строки. Я сам использую этот стиль.


Стиль Алмена

Впервые был употреблен Эриком Алменом в исходных кодах утилит для ОС BSD, поэтому иногда его называют "стиль BSD". Достаточно нагляден, но требует использования дополнительной строки.

Вот пример кода (обычно используется четыре пробела):

void f1(int i1) 
{
	if(i1==0) 
	{
		 printf("Hello, World");
	 }
}

Whitesmith

Использовался в IDE компилятора Whitesmith C. Более тесно улавливается связь кода и скобок.

Вот пример кода (обычно используется четыре пробела):

void f1(int i1) 
	{
	if(i1==0) 
		{
		printf("Hello, World");
		}
	}


GNU

Как следует из названия, является традицией кода, распространяющегося по лицензии GNU GPL. По моему мнению, чрезвычайно неэкономный гибрид стилей Алмена и Whitesmith.

Вот пример кода (обычно используется четыре пробела):

void f1(int i1) 
	{
		if(i1==0) 
		{
			printf("Hello, World");
		}
	}

В C скобки после операторов if, else, do, while и for ставить фигурные скобки необязательно. Тогда будет выполняться только следующий за оператором условия операнд. Давайте обратимся к коду:

if(i1!=0)
	printf("Hello World");
	printf("\n%i",i1);

На первый взгляд второй вызов printf будет выполняться при i1 не равном нулю. Но это не так. Кстати я делаю так:

if(i1!=0)	printf("Hello World");
	printf("\n%i",i1);

Также следует обратить вниание на обрамление кода пробелами. И это касается не только отступов в одном из вышеприведенных стилей, но и при описании выражений - cравните: a=b+c-d*f/g и a=b + c - d * f / g . В действительности, второй кусок более читаемый, однако некоторые программисты перебарщивают с пробелами. Мне кажется, что иногда следует воспользоваться нормами расстановки знаков препинания в печатном тексте и после и перед скобками пробелы опускать: a[index]++ и a [ index ] ++. По моему, второй пример ужасен!

А давайте посмотрим на кусочек из моего движка сайта (на PHP я тоже программирую):

 function show_category() {
                global $DB, $SITE;
                $num_cuts = 0;
                if(!isset($SITE->input['cmscat'])) {
                        // Не указан номер нужной категории
                        $SITE->error('невозможно показать категорию, потому что не указан необходимый параметр');
                        return 1;
                        }
	          $cat_name = $DB->fast_query('SELECT name FROM scl_cms_categories WHERE id='.$SITE->input['cmscat'].';', 'name');
                if($cat_name===0){
                        // Категория нет :)
                        $SITE->error('Невозможно отобразить категорию, потому что она отстутствует');
                        return 1;
                        }
           $SITE->begin_output('Категория статей "'.stripslashes($cat_name).'"');

		   $child = $SITE->input['cmscat'];
               $SITE->output .= '<div class="cmsstory" align="right">';  
               while($child!=0) 
                 $SITE->output .= '<a href="index.php?mod=cms&act=show_cat&cmscat='.$child.'">'.$this->get_parent($child).'</a> &bull; ';  
               $SITE->output .= '<a href="index.php">Чего-то там</a></div>';  

               $cats = array();
               $cats = $this->get_categories($cats, $SITE->input['cmscat']);
               $SITE->output.='<div class="catlinkshead">Подкатегории:</div><div class="catlinks">';

               foreach($cats as $c_id => $c_val) {
                 $SITE->output.='<a href="index.php?mod=cms&act=show_cat&cmscat='.$c_id.'">'.$c_val.'</a><br>';
               }

                if(!isset($SITE->input['segment'])) $segment=0;
                else $segment =  $SITE->input['segment'];
                $DB->query('SELECT id, name, author, added FROM scl_cms_data WHERE parentid='.$SITE->input['cmscat'].';');

               $SITE->output.='</div><div class="catlinkshead">Список статей:</div><table class="cattable">';

                while ( $DB->allow_get_rows == true) {
                    $DB->next_row(); 
                    if ( $DB->allow_get_rows == true) {
				$DB->row['added'] = $this->get_added($DB->row['added']);
                        $SITE->load_template('link_cms.htm',$DB->row);

     /*                   if($SITE->isadmin==true) {     $SITE->output.=' <td class="adminoptions"><a href="index.php?mod=cms&act=edit&id='.$DB->row['id'].'">Отредактировать</a>';
                        $SITE->output.=' <a href="index.php?mod=cms&act=del&cmsid='.$DB->row['id'].'">Удалить</a></td>'; }  */

                        } }
           $cat_description = $DB->fast_query('SELECT description FROM scl_cms_categories WHERE id='.$SITE->input['cmscat'], 'description');
                        // Описание группы 
                        $SITE->output.='</table><br />'.$this->parse(stripslashes($cat_description));
		   $DB->query('SELECT count(*) FROM scl_cms_data');
		   $DB->next_row();
           for($num_cuts = 0; $num_cuts < $DB->row['count(*)']; $num_cuts+=20) 
		   		$SITE->output.='<br /><a href="index.php?mod=cms&act=show_cat&cmscat='.$SITE->input['cmscat'].'&segment='.$num_cuts.'"> Страница '.strval($num_cuts/20+1).'</a>'; 
           if($SITE->isadmin==true) $SITE->output.='<br /><br /><div class="adminoptions"><a href="index.php?mod=cms&act=del_cat&cmscatid='.$SITE->input['cmscat'].'">Удалить</a></div>'; 
           $SITE->end_output();
  }

Попробуй разберись! Итак, мы убедились, что выбор стиля оформления кода очень важен.

Но особую роль играют правила наименования идентификаторов. Конечно же можно давать короткие имена переменным, функциям и классам (это, конечно же C++) и тсчательно комментировать их, однако это не всегда удобно в коде большого размера; сравните: названия идентификатора цикла (многим известный i) и название, отвечающий за текущее состояние (к ней как правило надо обращаться в разных кусках цикла). Следует также не переусердствовать, ведь некоторые программисты любят переводить смысл переменной на английский язык:

int CountOfSelectedStrings;

В таком случае надо прибегать к сокращениям.

Так как парсеры C-компиляторов чувствительны к регистру, то идентификатор пишут определенным стилем:

// GNU
int lower_case_with_underscores;

// C89/ANSI C
int lowercasefunc();

// C99
int _CapitalizedFunc();

// lower - префикс о них мы поговорим потом
int lowerCaseFirstLetterWithMixedCase; 

// По-моему, оптимальный вариант
int AllMixedCase;

// Честно говоря чрезвычайно неудобный стиль,
// однако хорош для визуального разделения типов и переменных
int UPPERCASENOSEPARSTOR;

// Смотри выше, опять же при записи UPPERCASE_SOMEINDEX: UNDER - префикс
int UPPERCASE_WITH_SEPARSTOR;
Я очень бы смеялся, читая такой код:
int draw_on_dcontexts(HDC hdc) {
 HDC Hdc, HDc, hDc, hDC, hdC, HdC;
 /*...*/
}

Теперь поговорим о префиксах. Конечноже многие знакомы с предложением программиста Microsoft Чарльза Симония (так называемая венгерская нотация, однако некоторые программисты окрестили ее трансильванской ересью). Суть ее заключалась в том, чтобы для каждого типа ввести единый префикс, и использовать его в дальнейшем. Например: int - n, char - ca. Конечноже удобно было бы знать, что переменная lpszString является строкой типа char*, однако я не сронник этой нотации, ведь многие программисты вводят свои типы и в зависимости от компилятора свойства переменных могут быть разными, например int, который теоретически вмещает число до INT_MAX не гарантирует точное соответствие 32 битам, и в тоже время никто не запрещает писать __int32 nValue; (BCB). А вот такое правило - для комнаты отдыха:

u - unsigned
n - int
l - long
i8 - short (если short занимает в памяти 8 бит)
i16 - int (если int занимает в памяти 16 бит)
i32 - __int32
i64 - __int64
imax - int с максимальным числом битов
b - bool
ie - enum :)
ch - char
wch - wchar_t
p - указатель
sz - массив символов
s - тип строки, определенный компилятором:
VC - CString
BCB - AnsiString, String
STL - string
etc.
e - double, float
g - double, float
le, lg - long double, long float

f - FILE*
h - дескрипторы

Кстати идентификаторы, начинающиеся с __, to, str и is противоречат стандарту.

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

(c) Статью написал Кляус Сергей.



 Design by Шишкин Алексей (Лёха)  ©2004-2008 by sources.ru