Оформление кода в 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> • ';
$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) Статью написал Кляус Сергей.
|