Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 26
Текст из файла (страница 26)
Я всегда обращаю внимание на подобного рода зависимости и рекомендую по возможности устранять их, или хотя бы минимизировать последствия. Почему вообще по этому поводу стоит беспокоиться? Людям, программирующим на разных платформах и применяющим разные компиляторы, ответ на вопрос очевиден; иначе придется тратить кучу времени, чтобы то и дело отыскивать трудноуловимые и крайне неочевидные ошибки. Люди же, ограниченные единственной системной платформой и единственным компилятором, чувствуют себя свободными от этой проблемы, утверждая, что «язык — это то, что реализует мой компилятор».
Я считаю эту точку зрения узкой и недальновидной. Действительно, ведь если программа добивается успеха на какой-то платформе, то ее с огромной долей вероятности будут портировать на иные аппаратно-программные платформы, и кому-то все равно придется возиться с устранением имеющихся несовместимостей. Кроме того, часто программы приходится компилировать разными компиляторами на одной и той же системной платформе, или, Глава 4. Типы и объявления 120 например, не исключена ситуация, когда новые версии одного и того же компилятора по-разному реализуют те или иные аспекты языка С++. Намного проще при первоначальном написании программного кода заранее выявить все источники системных несовместимостей и ограничить их влияние, чем потом разыскивать их в хитросплетениях готовой программы.
Ограничить влияние нюансов языка, зависящих от конкретной реализации, относительно несложно. Намного труднее ограничить влияние системнозависимых библиотек на переносимость программ. Везде, где только возможно, стоит использовать средства стандартных библиотек. Несколько целых типов, беззнаковые типы и типы со знаком, нескольких типов с плавающей запятой — все это предназначено для того, чтобы программист мог выжать максимум из аппаратных возможностей конкретного компьютера. Для разных типов компьютеров объем требуемой памяти, время доступа к ней и скорость вычислений существенно зависят от выбора фундаментального типа. Если вам знакома машинная архитектура, то несложно выбрать, например, подходящий целый тип для конкретной переменной.
Значительно труднее писать истинно переносимый низкоуровневый программный код. Размеры программных объектов в С++ кратны размеру типа с!гаг, так что фактически по определению размер типа сваг равен 1. Размер любого объекта или типа можно узнать с помощью операции з1геоЩб.2). Вот что гарантируется относительно размеров фундаментальных типов: ! егггеоГ(сьаг) <г1геоГ(гйог() < (геоГ(1п() < зггеоГ(1опк) ! < гггеоГ(Ьоо1) < гггео) (1оп8) гггеог(сьаг) < агеева велас 1) < зггеоГ(1опк) лгео)'(!)оаг) < лгео!'(аоиЫе) < г(геоГ(1опк с)оиЫе) г(геоГ(Ф) е г(геоГ(лкпей У) е зггеоГ(апг18пей М) Ке '] БЛ ~75 б сЬаг: Ьоо1: (л(: )п(*: Г 12345бзе34 дооЫе: сЬаг(14]: Не11о, ног1с(! 10 1 Здесь сможет быть с!гаг, з!гогГ те, гне или !оня 1пг.
Кроме того, гарантируется, что под с2гаг отводится минимум 8 бит, под зйогг или 1т — минимум 16 бит, под !вняв минимум 32 бита. Тип сйаг достаточен для представления всего набора символов, присущих машинной архитектуре. Представим графически типовой набор фундаментальных типов, а также конкретный экземпляр текстовой строки: 4.7.
Тип чо(б 11пс!щге <Дтйз> 1!пс!и»(е <1озо еат> !и! та(п () ( зЫ:: сои!« "!агяет)гоп!==" « т 1:: потек(с 11т1гз<))оаг>:: тах ( ) « ", сйаг 1з запев' == " «зЫ:: питепс Дт!(в<сваг>::1з з(бпеВ« ' ),п'; Разные фундаментальные типы можно свободно смешивать в выражениях (например, в присваиваниях). При этом, по-возмохсности, величины преобразуются так, чтобы не терялась информация 5С.б). Если значение е может быть точно представлено в переменной типа Т, то преобразование е к типу Тне ведет к потере информации, и нет проблем. Преобразований с потерей информации лучше всего просто не допускать (эС.б.2.б).
Для успешного выполнения больших программных проектов и для понимания практически важного стороннего кода требуется точное понимание процесса неявного преобразования типов (!тр11с11 сопкегзюп). Но для чтения последующих глав в этом нет необходимости. 4.7. Тип чоЫ Синтаксически тип юЫ относится к фундаментальным типам, но использовать его можно лишь как часть более сложных типов, ибо объектов типа юЫ не существует. Этот тип используется либо для информирования о том, что у функции нет возврата, либо в качестве базового типа указателей на объекты неизвестных типов.
Например: 7!ошибка: не существует объектов типа чош У ошибка: не существует ссылок на коЫ // функция)' не возвращает значения (з 73) д указатель на объект неизвестного типа (15,6) юЫ х; ьоЫь т юЫ Т(); ю(а'* рьч Объявляя функцию, вы обязаны указать тип ее возврата. С логической точки зрения кажется, что в случае отсутствия возврата можно было бы просто опустить тип возврата.
Это, однако, сделало бы синтаксис С++ менее последовательным (Приложение А) и конфликтовало бы с синтаксисом языка С. Поэтому юЫ используется в качестве «псевдотипа возврата», означаюшего, что фактически возврата нет. В сопоставимом масштабе (0.2 дюйма на байт) мегабайт памяти растянется вправо на три мили (около 5 километров). Размер типа сйаг выбирается так, чтобы он был оптимальным для хранения и манипулирования символами на данном типе компьютеров; обычно это 8 бит (один байт). Аналогично, размер типа Ыг выбирается с целью оптимального хранения и вычислений с целыми числами; обычно это 4-байтовое слово (32 бита). Неразумно требовать большего, однако ж, машины с 32-битным типом с))аг сушествуют.
Машинозависимые аспекты фундаментальных типов представлены в файле <11- т(м> (822.2). Например: Глава 4. Типы и объявления 122 4.8. Перечисления епит (АЮМ, АЮТО, ВВЕАК); определяет в качестве элементов перечисления три целые константы, которым не- явно присваиваются целые значения. По умолчанию, целые значения присваивают- ся элементам перечисления в возрастающем порядке, начиная с нуля, так что АЯМ == О, АБТО == 1 и ВВЕАХ == 2. Перечисление может иметь имя, например: епит Ьеуыогд ( АЯМ, АВТО, ВВЕАК); Каждое перечисление является отдельным типом. Типом элемента перечисления является тип самого перечисления.
Например, тип АБТО есть кеунюгд. Если переменная объявляется с типом кеуногФ (тип перечисления), а не просто 1п(, то это дает пользователю и компилятору дополнительные сведения о предполагаемом характере использования этой переменной. Например: го(дТ(аеунюгд йеу) ( знйсЬ (Ьеу) ( саве А5М: УУ некоторые действия Ьгеай! сазе ВВЕАК: Уl некоторые действия Ьгеай! Здесь компилятор может выдать предупреждение о том, что используются лишь два из трех элементов перечисления.
Элементы перечисления можно явно инициализировать константными выражениями (ЭС.б) интегрального типа (Э4.1.1). Когда все элементы перечисления неотрицательные, диапазон их значений равен [О;2х-Ц, где 2" есть наименьшая степень двойки, для которой все элементы перечисления попадают в указанный диапазон. При наличии отрицательных элементов этот диапазон равен (-2";2"-!]. Диапазон определяется минимальным количеством бит, требуемых для представления всех значений элементов (отрицательные значения — в дополнительном коде).
Например: УУ диапазон 0:1 УУ диапозон 0:15 У диапазон -104857б:1048575 епит е1 (Йога, ЙКЫ); епит е2 (а = 3, Ь = 9) ! епит еЗ (пип = -10, тах = 1000000); Значение интегрального типа может быть явно преобразовано к типу перечисления. Если при этом исходное значение не попадает в диапазон перечисления, то результат преобразования не определен. Например: Перечисление (епитега!1оп) является типом, содержащим набор значений, определяемых пользователем (программистом). После определения перечисление используется почти так же, как и целые типы. Именованные целые константы служат элементами перечислений.
Например, следующее объявление 4.9, Объявления епит1<аВ (х=1, у=2, а=4, е=В ) <,Удиапазон 0<!5 !!ошибка типа: 5 не принадлежит к типу))аВ У)о<<.'))аВ(5) принадлежит типуЯаВ и входит в его диапазон УУ о!<: !<аВ(!2) принадлежит типу))аВ и входит в его диапазон У не определено: 99 не входит в диапазон типа))аВ 1<аВ 17 =5 < 1<аВ)2=()аВ (5) ! 3<аВ13=9аВ (<) е) ! 1<аВ)4=1<аВ (99) < 4.9.
Объявления Прежде, чем имя (идентификатор) будет использоваться в программе, оно должно быть объявлено. То есть должен быть специфицирован тип, говорящий компилятору о том, к какого рода сущностям относится поименованный программный объект. Вот примеры, иллюстрирующие разнообразие возможных объявлений: слог сЬ! зозпВ з< Ьи соил<=1; сопл< доиЫе р<'=3. 141 5926535В9 7932385 < ех<егп!и< еггог питЬег! сопл< слог* пате = "Ща<" < сони сааг* зеазоп() = ( "зрг<пВ", "витте<", "1аИ", "ыш<ег"); в<гас< Ра<е(ш< 4, т, у; ) ! 1п< дау(Разе' р) (ге<игл р->4! ) «оиЫе ляг< (ИоиЫе); <етр1а<е<с1азз Т> Т аЬз ( Т а ) ( ге<игл а <О 2 -а < а; ) <уреде1' сотр!ехкзлог<> Рот« м<ис< ()лег< епит Веег (Саг!зЬегВ, ТиЬогВ, ТЬог); патезрасе )чЮ ( ш<а! ) Последнее присваивание наглядно показывает, почему не допускаются неявные преобразования целых в перечисления — просто большинство целых значений не имеют представления в отдельно взятом конкретном перечислении.
Понятие диапазона перечисления в языке С++ отличается от понятия перечисленин в языках типа Ровса!. Однако в языке С и затем в языке С++ исторически закрепилась необходимость точного определения диапазона перечислений (не совпадающего со множеством значений элементов перечисления) для корректного манипулирования битами. Под перечисления (точнее, под переменные этого типа) отводится столько же памяти (результат операции з)еео1), что и под интегральный тип, способный содержать весь диапазон перечисления. При этом указанный объем памяти не больше, чем з(хеоу(1п<) при условии, что все элементы перечисления представимы типами шт или илз(Впед та Например, з!ееоу(е1) может быть равен 1 или 4, но не 8, на компьютере, для которого з(хео1((п<) == 4.
При выполнении арифметических операций перечисления автоматически (неявно) преобразуются в целые (96.2). Перечисления относятся к определяемым пользователем типам, так что для них можно задавать свои собственные операции вроде чзили «(91).2.3). Глава Я. Типы и объявления 12Я Как видно из представленных примеров, объявления могут делать нечто большее, чем просто связывать имя с типом.