Иллюстрированный самоучитель по практике программирования

Язык

Придерживайтесь стандарта.
Первое, что необходимо для создания переносимого кода, – это, конечно, использование языка высокого уровня, причем его стандарта, если таковой определен. Двоичные коды переносятся плохо, исходный код – несколько лучше. Однако и для него трудно, даже для стандартных языков, описать точно, каким именно образом компилятор преобразует его в машинные инструкции. Очень немногие из распространенных языков представлены единственной реализацией: обычно имеется множество производителей различных версий для различных операционных систем, которые к тому же со временем изменяются. Каждая версия будет обрабатывать ваш код по-своему.

Почему стандарт не является строгим описанием? Иногда стандарт неполон и не описывает отдельных специфических случаев. Иногда он намеренно неконкретен: например, тип char в С и C++ может иметь знак, а может и не иметь; он даже не обязательно должен быть 8-битовым. Подобные детали оставлены на усмотрение создателя компилятора; в этом есть свои плюсы: стимулируется появление новых эффективных реализаций; снимаются ограничения, накладываемые на железо, но жизнь программиста, конечно, несколько усложняется. Вообще, степень проработанности стандарта зависит от многих глобальных причин. И наконец, нельзя забывать, что языки достаточно запутанны, а компиляторы весьма сложны; в них могут быть неправильности интерпретации и ошибки реализации.

Иногда же языки вообще не стандартизованы. Официальный стандарт ANSI/ISO С был принят в 1988 году, а стандарт ISO C++ ратифицирован только в 1998-м. На момент написания этой книги не все из распространенных компиляторов поддерживают это официальное описание. Язык Java сравнительно молод, и его стандарт можно ждать только через несколько лет. Вообще стандарт языка разрабатывается только после того, как создаются несколько конфликтующих* версий, которые надо унифицировать, а сам язык получает достаточно широкое распространение, оправдывающее затраты на стандартизацию. А между тем по-прежнему надо писать программы и поддерживать в этих программах различные среды исполнения.

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

? *х[] = {"abc"};

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

Следуйте основному руслу.
Неспособность некоторых компиляторов выявить ошибку в приведенном примере весьма прискорбна, но зато мы теперь сможем осветить важный аспект переносимости. У каждого языка есть свои непроработанные моменты, точки зрения на которые разнятся (например, битовые поля в С и C++), и благоразумие подсказывает избегать их использования. Стоит использовать только те возможности, описание которых недвусмысленно и хорошо понятно. Очевидно, что именно такие возможности, скорее всего, будут широко доступны и реализованы везде одинаковым образом. Мы называем их основным руслом (mainstream) языка.

Трудно однозначно определить, какие именно конструкции входят в это основное русло, но зато те, что в него не входят, определить просто. Совершенно новые возможности, такие как complex или комментарии // в С, или возможности, специфичные для конкретной архитектуры, вроде ключевых слов near и far, обязательно создадут вам проблемы. Если что-то в языке настолько необычно или непонятно, что для того, чтобы разобраться, вам приходится консультироваться с "языковым правоведом" – экспертом по чтению его описаний, не используйте такую возможность вовсе.

В своем обсуждении мы сконцентрируем основное внимание на С и C++. широко распространенных и универсальных языках. Стандарт С существует уже более десятка лет, и язык очень стабилен, правда, разрабатывается новый стандарт, так что впереди очередные подвижки. А вот стандарт C++ появился совсем недавно, и потому еще не все реализации этого языка приведены с ним в соответствие.

Что можно считать основным руслом в С? Этот термин обычно относят к установившемуся стилю использования языка, но иногда лучше принимать во внимание грядущие изменения. Например, первоначальная версия С не требовала создания прототипов функций. Описание функции sqrt выглядело при этом так:

? double sqrt();

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

do.uble sqrt(double);
Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.