Pull to refresh

Будь проще, и люди потянутся к тебе, или как полюбить Си, сохранив рассудок

Reading time5 min
Views15K
Я по жизни самоучка: если не считать FORTRAN и PL/1, которым меня «учили» в ВУЗе (надеюсь, понятно, как давно это было?), основную часть своих знаний по программированию я получил исключительно методом самообразования. Начинал, как водится, с Pascal и логичным его продолжением Delphi. По мере изменения приоритетов осваивал ассемблер 86-го семейства (MS DOS), а затем, по мере увлечения микроконтроллерами освоил ASM51 и AVR Assembler. Все это давалось мне достаточно просто, т.к. все перечисленное поддается весьма простому и, главное, четко структурированному логичному описанию — я имею ввиду синтаксис и принципы языка.

Закономерно неизбежным был и следующий шаг — переход на Си (подчеркну — в моем случае речь именно о программировании микроконтроллеров), и тут, как говорится, процесс застопорился. Синтаксис Си — та еще штучка, книги по этому языку — отдельная песня. Даже у классиков K&R с первой же главы на неокрепший мозг начинающего обрушивается «работающий helloword», при том что ни единого слова о синтаксисе, об операторах и т.п. сказано не было! Когда я начал изучать стандарт языка Си, у меня возникали суицидально-депрессивные состояния, ибо ранее устаканившееся в мозгу понятие стандарта, как четкого регламента, было разрушено почти полностью!

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

Старался использовать имеющееся чувство юмора — рекомендую при чтении его не отключать.

Для начала речь пойдет о, казалось бы, самом простом: типах данных и описании переменных этих типов.
Все знают, что описание int var; описывает переменную var типа int, соответственно расширенное описание int var1, var2, var3; описывает уже три переменных одинакового типа. Здравый смысл и жизненный опыт подсказывают, что описание переменной или нескольких переменных состоит из двух частей: на первом месте стоит идентификатор типа (int), а затем, через пробел, один или более (списком через запятую) идентификаторов переменных.
Все так, и пока все понятно. Но Си позволяет нам добавлять к описанию переменных еще и некоторые префиксы, например, const, означающий, что переменная не должна менять свое значение. Кстати, есть и другие префиксы, например, auto, но не о них сейчас речь. Причем как это ни удивительно, префикс может стоять как перед типом, так и после него:
const int var1;
и
int const var1;
полностью идентичные записи.

Не знаю, как у вас, но мой мозг начинает недовольно ворочать извилинами, т.к. не выходит составить четкое правило описания переменных, как было сказано ранее (см. выделенный текст выше). Получается что-то типа: описание переменной заключается в том, что перед ее идентификатором или списком идентифкаторов разделенные пробелами могут находиться идентификаторы типа и/или префикс, указывающий на особенности применения и использования переменной. Обратите внимание, что я написал могут находиться и использовал союз или – это на самом деле так: язык Си допускает отсутствие идентификатора типа переменной, в этом случае тип по умолчанию считается int. Т.е. допустима и такая запись: const var;

Пробуем плавно перейти к указателям.
const int *var; — указатель на переменную, которая не может менять значение.
int const *var; — указатель, который не может менять свое значение, на переменную int, которая может…

Ваш мозг еще не взорвался? Мой уцелел только благодаря охлаждающему компрессу: только что казалось, что тип и префикс могут располагаться друг относительно друга в произвольном порядке, а теперь выясняется, что порядок существенно меняет смысл описания! Да еще и правильное прочтение строки int const *var; не слева направо, не справа налево, а какой-то спиралью: начинаем с середины (указатель, т.е. *), потом идем налево (const, т.е. указатель не меняющий значение), потом направо (вспоминаем идентификатор переменной var), а потом снова налево (тип переменой int). Бррр…

А теперь попробуйте описать без ошибок указатель, не меняющий свое значение, на указатель, который может меняться, указывающий на неизменяемую переменную типа int… Каково?! Думаю, на этом этапе немало начинающих бросали в стенку книжку по Си и шли пить пиво.

И это мы еще не брались за указатели на функции, массивы указателей, указатели на массивы указателей на функции…
В общем, страшная это вещь – язык Си! С одной стороны требует четкости изложения своих мыслей от программиста, с другой позволяет использовать столько возможностей, допущений, разрешений и вариантов, что просто затруднительно сохранить эту самую четкость мыслей… Да еще любимое занятие большинства авторов книг по Си перечислять все эти (я-то еще не все варианты и нюансы упомянул!) варианты с комментариями в том же духе: Си устанавливает следующий порядок действий …, но в случае если …, допускается …, хотя при этом … правило изменяется на … — вместо многоточий подставить нужное.

Я, конечно, понимаю, что Си – инструмент широких возможностей, и, наверное, каждая из возможностей и каждый из нюансов служат для достижения каких-то по-настоящему важных целей, но ведь не каждый должен быть гуру! Как же быть обычному человеку, для которого программирование – хобби?

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

Грамотное применение этой возможности, с моей точки зрения, заключается в простом соблюдении единственного правила: избегать описаний типов из двух и более следующих подряд ключевых слов (идентификаторов). Таким образом, вы должны стремиться к единственной форме определения переменной: слева один идентификатор типа, через пробел правее – идентификатор или список идентификаторов переменных. Все другие допускаемые языком варианты мы [смиренно и добровольно] исключаем из обихода.

На практике это выглядит так.
const int var; — для описания приходится использовать 2 идентификатора, т.е. наш случай. Вводим новый тип для этого:
typedef const int const_int;
B определение нашей переменной превращается в идеальное
const_int var;
строго по нашему правилу.

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

Если мы хотим определить указатель на этот новый тип, можем записать так
const_int *var; и это будет так же просто и однозначно, но можем и ввести новый тип:
typedef const_int* const_int_ptr;
и получить «идеальную» запись
const_int_ptr var;

Теперь вспомним о том описании, которое вводило в ступор ранее: указатель, не меняющий свое значение, на указатель, который может меняться, указывающий на неизменяемую переменную типа int. С учетом только что сделанного получится так:
typedef const int const_int; // неизменяемая переменная int
typedef const_int* const_int_ptr; // указатель (изменяемый) на неизменяемую переменную int
typedef const const_int_ptr* const_ptr; // неизменяемый указатель на предыдущий указатель
тут уже можно остановиться, но можно и продолжить:
const_ptr var; // а это уже конечное описание переменной

Разве это сложно для понимания? Как с моей точки зрения, так гораздо проще, чем упихивание всех этих префиксов-суффиксов в одну строку… Ввод новых типов облегчит жизнь и в случае, если потребуется передавать данные этих типов в качестве параметров функций или возвращать их в виде результата.

К чему я призываю? Да вот к чему: коллеги-программисты и те, кто хочет ими стать! Будьте проще, не кичитесь своими знаниями глубин Си и не погружайтесь в эти глубины без необходимости, ограничивайтесь самыми простыми (как в записи, так и в восприятии) конструкциями для достижения своих целей! И тогда люди к вам потянутся!
Tags:
Hubs:
Total votes 37: ↑18 and ↓19-1
Comments36

Articles