Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 29
Текст из файла (страница 29)
(* !.5) Напишите программу, которая выводит размеры фундаментальных ти- пов, нескольких типов указателей и нескольких перечислений по вашему вы- бору. Используйте операцию з(аео)". (*1.5) Напишите программу, которая выводит буквы 'а' — 'г' и цифры ' О' — '9' и их десятичные коды. Повторите все для иных символов, имею- щих зрительные образы. Выведите числовые коды в шестнадцатеричном виде. (*2) Каковы на вашей машине минимальные и максимальные значения для следующих типов: сваг, зйогг, (яг, 1опя, г?оаб г(оиЫе, 1ояя йоиЫе и ипз(яяег1? (*1) Какова максимальная длина локальных имен на вашей машине? Какова максимальная длина для внешних имен на вашей машине? Есть ли ограниче- ния на символы, которые можно использовать в именах? (*2) Нарисуйте граф для фундаментальных типов, поддерживаемых любой стандартной реализацией (стрелка указывает направление от первого типа ко второму, если все значения первого типа представимы переменными второго типа).
Нарисуйте тот же граф, но для вашей любимой реализации. Указатели, массивы и структуры Указатели — нуль — массивы — строковые литералы — константы — указатели и константы — ссылки — во1д* — структуры данных — советы — упражнения.
5.1. Указатели Для заданного типа Т, тип Т' является «указателем на Ть. Это означает, что переменные типа Т' содержат адреса объектов типа Т. Например: сйас с = 'а'; сйаг* р = ъсз Пр содержит адрес с или в графической форме: К сожалению, указатели на массивы и указатели на функции требуют более сложных конструкций объявления: Подробное объяснение этого синтаксиса дано в В4,9.1, а полное изложение грамматики — в Приложении А.
1пг* )из сйаг** ррс; тг* ар [15]; 1пз("Гр) (сваг*) ) 1пз* Т(сааг*); Возвышенное и нелепое часто столь тесно переплетены, что их трудно рассматривать порознь, — Том Пэйн 11 указатель на т( 0 указатель на указатель на сваг 11 массив из 15 указателей на т( д указатель на функцию с аргументом сваг* и возвратом (п( З функция с аргументом сваг«и возвратом "указатель на и(" Глава 5. Указатели, массивы и структуры 134 Основной операцией над указателями является разыменование (деге7вгепс!п8), то есть обращение к объекту, на который показывает указатель.
Эту операцию также называют косвенным обращением (!пйгест!оп). Унарная операция разыменования обозначается знаком *, применяемым префиксным образом по отношению к операнду. Например; слог с = 'а'; слог* р = ас; сйаг с2 = 'р; бр содерзкит адрес переменной с Рс2 == 'а' Указатель р показывает на переменную с, значением которой служит 'а'. Таким образом, значение выражения *р (присваиваемое с2) есть 'а'. Допускается ряд арифметических операций над указателями на элеменгпы массивов (85.3). Указатели на функции могут быть чрезвычайно полезны; они обсуждаются в 87.7.
Указатели задуманы с целью непосредственного использования механизмов адресации памяти, реализуемых на машинном уровне. Большинство машин умеют адресовать отдельные байты. А те, что не могут, умеют извлекать байты из машинных слов. В то же время, редкие машины в состоянии адресовать отдельные биты. Следовательно, наименьший объект, который можно независимо разместить в памяти и которым можно манипулировать через встроенные указатели, есть объект типа айаг. Стоит отметить, что тип Ьоо! требует по меньшей мере столько же памяти, что и тип сйаг (84.6). Для более компактного хранения малых объектов, можно использовать побитовые логические операции (86.2.4) или битовые поля в структурах (8С.8.1). 5.1.1.
Нуль сопл!!пг !УШИ = О; Модификатор сопя! (85.4) предотвращает случайное изменение АГШХ и гарантирует, что !У(НХ можно использовать всюду, где требуется константа. Нуль (0) имеет тип !пг. Благодаря стандартным преобразованиям (8С.6.2.3), нуль можно использовать в качестве константы любого интегрального типа (84.1.1), типа с плавающей запятой, указателя или указателя на член класса. Тип нуля определяется по контексту. Указатель со значением 0 называется нулевым указателем (пи!! ро!птег) и обычно (но не всегда) представим на машинном уровне в виде последовательности нулевых битов соответствующей длины.
Не существует объектов в памяти с адресом нуль. В результате, нуль служит литералом указательного типа, означающим, что указатель с таким значением не ссылается ни на какой обьект. В языке С было принято использовать макроконстанту АГАХ для обозначения нулевых указателей. Из-за более плотного контроля типов в языке С++, использование простого О вместо так нли иначе определенной макроконстанты АгйТ..6 приводит к меньшим проблемам.
Если вы чувствуете, что вам все же нужен АГ(Ш, вос- пользуйтесь 5.2. Массивы 5.2. Массивы Для заданного типа Т, тип 7[в[хе] является «массивом из в]ее элементов типа Т», Индексация (нумерация) элементов требует индексов от 0 и до в]хе-1. Например: 1(ааг «[31; сваг* а[32]; Количество элементов массива (размер массива) должно задаваться константным выражением (зС.5). Если же нужен массив переменного размера, воспользуйтесь типом «есгаг (в3.7.1, э]6.3). Например: Многомерные массивы определяются как массивы массивов.
Вот соответствую)ций пример: ьпе 42(101 [гО]; Если здесь, как это принято во многих других языках программирования, отделять запятой различные размеры массива, то возникнет ошибка компиляции, ибо запятая в языке С++ обозначает операцию следования (эеаиепс/пя орегагог, э6.2.2), и которая неприменима в константных выражениях (зС.5). Можете проверить это на следуюшем примере: (пе Ьай [ 5, 21; Многомерные массивы рассматриваются в зС.7.
В коде высокого уровня их лучше вообше избегать). 5.2.1. Инициализация массивов Массив можно проинициализировать списком значений. Например; аи «1[] = (1,2,3,4); сваю «2[1 = ('а', 'Ь', 'е',0); Когда массив объявляется без явного указания размера, но со списком инициализируюших значений, размер вычисляется по количеству этих значений. Следовательно, «1 и «2 имеют типы Ьвг[4] и сйаг[4], соответственно. Когда размер массива указан явно, а в инициализируюшем списке присутствует большее количество значений, то возникает ошибка компиляции. Например: сваг «3[2] = ( 'а', 'Ь', О ); //вегой елишком. иного инициализаторов сваг «4(3] = ( 'а', 'Ь', 0 ); /о/с Если же в инициализируюшем списке значений не хватает, то «оставшимся не у дел» элементам массива присваивается 0. Например: ! Трудно согласиться с данным тезисом автора — разве матричная математика не относится к высокоуровневому коду? — Прим. ред.
«а14 ('( тг 1) ( 1пг «1 [11; «еегаг<(пг> 12 (!); ) Пмаееив иэ трех элементов типа)]оаи «(О(, «(1), «(2( //маееив иэ 32 указателей на елаг: а(0) .. а(31) /У ошибка: размер массива не константное выражение // о)г (все нормально) гУ 42 есть массив иэ 1О массивов по 20 целых в каждом П еп от запятая недопустима в константных выражениях 136 Глава 5. Указатели, массивы и структуры (пт 5[В) = (1,1,3,4) я Эквивалентно !пт г5[) = (1,1,3,4,0, О, О, О); Заметим, что присваивания списком значений для массивов не суи(ествует: гоЫг" ( ) ( г4 = ( 'с', '4', О); чегго: такое присваиввние для массивов недопустимо ) Если нужно подобное присваивание, воспользуйтесь стандартными типами гесгог (в(6.3) или га!аггау Я22.4).
Массив символов удобно инициализировать строковыми литералами Я5.2.2). 5.2.2. Строковые литералы Строковым литералом (звтпя 1[тега() называется последовательность символов, заключенная в двойные кавычки. "тй(з Ы а мг(пдв Строковые литералы содержат на один символ больше, чем это кажется на первый взгляд: они оканчиваются нулевым символом ' О', числовое значение которого равно нулю. Например: з(сеог( "Войс" ) ==5 Тип строкового литерала есть «массив соответствующего количества константных символов», так что литерал "Войс" имеет тип сопят сйаг [5] .
Строковый литерал допускается присваивать указателю типа сваг*, так как в более ранних версиях языков С и С++ типом строковых литералов был сйаг*. Благодаря этому допущению миллионы строк ранее написанного на С и С++ кода остаются синтаксически корректными. Тем не менее, попытка модификации строкового литерала через такой указатель ошибочна: Такого рода ошибки обычно не выявляются до стадии выполнения программы, и к тому же, разные реализации по-разному реагируют на нарушение этого правила (см. также вВ.2.3).
Очевидность константного характера строковых литералов позволяет компиляторам оптимизировать как методы хранения этих литералов, так и методы доступа к ним. Если нам нужна строка с гарантированной возможностью модификации, то нужно скопировать символы в массив; гоЫг"() ( сваг р [] = "вено"; о'р есть массив из 5 сваг гоЫ г"() ( сйаг* р = "р(ато"; р[4] = 'е'; ) й'еггог: присваивание константе; резулыпат не определен 5.2. Массивы 137 !! ОЬ р(О) = В; ) Память под строковые литералы выделяется статически, поэтому их возврат из функций безопасен.
Например: сопл! сааг* еггог теззаве (тг г) ( !!.. гезигп ьгапве еггог" г ) Память, содержащая строку "сопле еггог", никуда не денется после выходя из функции еггог тевзаяе О . От конкретной реализации компилятора зависит, будут ли два одинаковых строковых литерала сосредоточены в одном и том же месте памяти (ВС.)). Например: сопл! сааг* р = "Негас1Ьиз" г сопи( сааг* ц = пНегас!(гизи г гоЫв() ( г) (р==д) сои!«" опе! ~пьг о результат зависит от конкретной реализации ) Обратите внимание на то, что для указателей операция == сравнивает адреса (значения указателей), а не адресуемые ими величины. пустан строка (етргу згг!пя) записывается в виде смежных двойных кавычек (" ") и имеет тип сопт сйаг(!). Синтаксис, использующий обратную косую черту для обозначения специальных символов (ВСРь2), применим и в строковых литералах. Это позволяет вносить в содержимое строковых литералов и двойные кавычки ("), и символ обратной косой черты (г).