Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 30
Текст из файла (страница 30)
Чаще всего, конечно, используется символ 'гп' для принудительного перевода строки вывода. Например: сои!«ьЬеер а! где епг! от теззадего го" г Символ 'а' из АБСП-таблицы называется ВЕз. (звонок); он приводит к подаче звукового сигнала. Переходить же на новую строку исходного текста программы при записи содержимого строкового литерала нельзя: "гЬ(з 1з но! а згг!нл Ьи( а лунгах еггог" С целью улучшения читаемости текста программы длинные строковые литералы можно разбить на фрагменты, отделяемые друг от друга лишь пробельными символами'.
Например: сааг а1раа ( ) = "аасг!еЯЬг)Мтпорцгзгигггхуг" ьАВОЗЕГОН!ЯКйМНОРЦКБТ(1Ь И'ХТХь Г г В том числе, и переходом на новую строку текста программы. — Прим. ред. тзв Глава 5. Указатели, массивы и структуры Компилятор объединяет смежные строковые литералы в один литерал, так что переменную а]р))а можно было бы с тем же успехом инициализировать единственной строкой: "ободе(8Ь«)й1т порус«ги»нху«АВСРЕРСН!ТКЕМ(УРР(гВЯТЮ И»ХУУ" ( 5.3. Указатели на массивы В языке С+-ь указатели и массивы тесно связаны.
Имя массива может быть использовано в качестве указателя на его первый элемент. Например: 1пг г[] =(1,2,3,4); 1пг* р1=»; Вука«атель на начальный элемент Гнеявное преобразование) 1пг* р2=«»[О]; У(указатель на начальный элемент (пг* РЗ=аг [41; У(указатель на элемент, следующий за последним или в графической форме: р1 рг рЗ [( [2 Г]Т4~ Гарантируется возможность настройки указателя на «элемент, расположенный за последним элементом массива». Это важный момент для многих алгоритмов [82.7.2, 818.3).
Однако поскольку такой указатель на самом деле не указывает ни на какой элемент массива, его нельзя [не стоит) использовать ни для записи, ни для чтения. Возможность получения осмысленного адреса «элемента, расположенного перед первым элементом массива», не гарантируется и таких действий следует избегать. В рамках некоторых машинных архитектур память под массивы может выделяться в самом начале адресного пространства, так что адреса «элемента, расположенного перед первым элементом массива» может просто не сушествовать. Иеявные преобразования имени массива в указатель на его первый элемент широко используются при вызовах функций в С-стиле.
Например: вк(егп "С" тг «ичвп (соп«1 сваг*); УУ из <«(г(пб.б гоЩ'( ) ( сйаг »[1 = »Аппетаг(е"; сваг* р = г; гУ неявное преобразование от сйаго к сйаг» «а1еп (р); «п1вп (»); у=р; УУ неявное преобразование от спагЦ к сбаг» гУ еггог: неверное присваивание массиву В состав строк можно включать нулевые символы, но большинство функций и не догадаются о том, что после нулей в них есть что-то еше. Например, строка "Хеп«~ ОООМип1с» будет трактоваться как "Хеп««такими библиотечными функциями, как «тгсруО или «П1еп() [см. 820.4.1). Строки с префиксом Х, например Х" апВ«1", состоят из символов типа ьгс(гаг 1 [84.3, 8С.З.З).
Тип этих строк есть сапа нсйаг 1[]. 5.3. Указатели на массивы 139 Здесь в обоих вызовах стандартной библиотечной функции зМеи () передается одно и то же адресное значение. Заковыка в том, что избежать такого неявного преобразования нельзя, так как невозможно объявить функцию, при вызове которой копировался бы сам массив к К счастью, не существует обратного (явного и неявного) преобразования от указателя к массиву. Неявное преобразование от массива к указателю, имеющее место при вызове функции, означает, что при этом размер массива теряется. Ясно, что, тем не менее, функция должна каким-то образом узнать размер массива, иначе невозможно будет выполнить осмысленные действия над ним. Как и другие функции из стандартной библиотеки языка С, получающие указатель на символьные строки, функция лМеа() полагается на терминальный нуль в качестве индикатора конца строки; ьМеа (р) возвращает количество символов в строке вплоть до, но не считая терминального нуля.
Все это достаточно низкоуровневые детали. Типы юсгог (916.3) и лМля (глава 20) из стандартной библиотеки не страдают этими проблемами. 5.3.1. Доступ к элементам массивов Эффективный и элегантный доступ к элементам массивов (и другим структурам данных) является ключевым фактором для многих алгоритмов (83.8, глава 18). Такой доступ можно осуществлять либо через указатель на начало массива и индекс элемента, либо непосредственно по указателю на элемент.
Например, следующий код, применяющий индексы для прохода по символам строки юЫ1[(сйаг г[1) ( 1ог([н( Г = О; о[11 (=О; Г++) ~У ислольэуетсл 1 Я эквивалентен коду, применяющему с той же целью указатель: юЫ(р(сйаг г[) ) ( Гог(сваг* р = г; *р! =О; рь+) ~7 иснользуетсл *р; ) Унарная операция разыменования (операция *) действует так, что *р есть символ, адресуемый указателем р, а операция ь+ инкрементирует значение указателя с тем, чтобы он показывал на следующий элемент массива.
Не существует фундаментальных причин, по которым одна из этих версий кода была бы эффективнее другой. Современные компиляторы должны превращать оба варианта в одинаковые фрагменты машинного кода (85.9(81). Программисты вольны осуществлять выбор между ними, исходя из логических или эстетических соображений. Результат воздействия на указатели арифметических операций +, —, ь+ и — зависит от типа объектов, на которые указатель ссылается. Когда арифметическая операция применяется к указателю р типа Т", предполагается, что р указывает на элемент массива объектов типа Т, р+1 указывает на следующий элемент этого массива, ар-1 — на предыдущий элемент.
Это подразумевает, что адресное (целочисленное) 140 Глава 5. Указатели, массивы и структуры значение выражения р+1 на з!хео7(2) больше, чем величина указателя р. Например, результатом выполнения кода ()1пс(иде <[озгееат> 1пг та1п () ( 1пт е! [10]; зйогг кз [10]; згд:: сои!«а е! [ 0] « ' ' «хе! [1) « ' т и '; згд:: сои!«а ге [ 0] « ' ' «аш [1] « ' Хп ' 1 ) будет Ох7Щае70 Охи"ае~4 ОхЩаедс Ох7фаеде ео!д 1'() ( (пг у1 [10] / !пг 2[10]; ш1!1 = ае! [5] -ак1[3]; //11 = 2 шг 12 = ау! [5] -ае2 [3); //резулыпат не определен !пг*р1 = 2 2; !пг*р2= 2-2; Рр! = &т2Я; // *р2 не определен Обычно, в использовании более-менее сложной арифметики указателей необходимости нет, и ее лучше избегать.
Сложение указателей не имеет смысла вообще; в итоге оно запрещено. Массивы не являются самоописываемыми сушностями, так как их размеры не хранятся вместе с ними. Это предполагает, что для успешного перемешения по элементам массива нужно так или иначе заполучить его размер. Вот пример на эту тему: при использовании принятой по умолчанию шестнадцатеричной формы записи значений указателей.
Из данного примера видно, что в моей реализации з!Севу(зйогг) есть 2, а з)хео!'(!пг) есть 4. Вычитание указателей друг из друга определено для случая, когда оба указателя показывают на элементы одного и того же массива (язык, правда, не предоставпяет быстрых способов проверки этого факта).
Результатом вычитания указателей будет количество элементов массива (целое число), расположенных между указуемыми элементами. К указателю можно прибавить или вычесть целое; в обоих случаях результатом будет указатель. При этом, если полученный таким образом указатель не указывает на один из элементов того же массива (или на элемент, расположенный за последним элементом массива), то результат его применения не определен. Например: 5.4. Константы «огаур (е!ьаг «( ], ипв10пей шг в(ее) ( Го«(!н( !=От !<мхе; (ь.ь) // используется 1Я сонМ !п1 % = гг сваг «2 (Ж]; гог(ш( 1=0; 1<%; тьь) // используется 12Я ) Заметим, что большинство реализующих язык С++ программных средств не предоставляет никаких способов контроля границ массива.
Такая концепция массивов весьма низкоуровневая. Более интеллектуальные концепции массивов можно реализовать с помощью классов 63.7.]). 5.4. Константы сопл! !нг тоИе! = 90! // тов(е! является константой сопл! ш( «(] = (1,2,3,4); //1Я являются константами еопвг 1пгх; //етгоп нет инициализатора Объявление объекта константным гарантирует, что значение объекта не изменится в его области видимости: «оЫ!() ( тоде! = 200; (г]++; // ее«от // в«тот Заметим, что ключевое слово сопи модифицирует именно тин, то есть оно ограничивает способы использования объекта, а не его размещение в памяти.
Например: «оЫО(соня(Х* р) ( // здесь нельзя модифицировать *р В языке С++ реализована концепция определяемых пользователем констант, явно отражающая тот факт, что значение нельзя изменять непосредственно. Это полезно во многих отношениях. Например, существуют объекты, которые не требуют изменений после их инициализации; использование символических констант обеспечивает более удобное сопровохсдение кода, чем в случае непосредственного применения литералов в коде программы; указатели часто используются только для чтения, а не для записи; большинство параметров функций предназначены лишь для чтения, а не лля перезаписи.
Ключевое слово сопя! используется Лля объявления константного объекта. Так как константным обьектам присваивать значения нельзя, то они в обязательном порядке должны инициализироваться. Например: 14г Глава 5. Указатели, массивы и структуры ио14 Ь () ( Х иа1' д(ава1) Уl ... ) УУ1аl л~ожно изменять В зависимости от своего «интеллектаь, конкретные компиляторга могут по-разному использовать факт константности объекта.
Например, инициализатор константного объекта часто (но не всегда) является константным выражением (йС.5); если это так, то его можно вычислить во время компиляции. Далее, если компилятор может выявить все случаи использования константы, то он может не выделять под нее память. Например: сопле (пг с1 = 1; сопле 1пг с2 = 2; солт те сЗ = ту 1(З); Узначение сЗ не известно в момент колтиляции ехгегп сопле 1пг с4; УУ значение с4 не известно в момент компиляции сопле 1пг* р = ьс2г УУ под с2 нужно выделить память 1пг в [тах); во14 Г'(1пг 1) ( зтйсЬ (1) ( сазе а: УУ... сазе Ь: Так как компилятор знает значения переменных с1 и с2, он может использовать их в константных выражениях.
Поскольку значения для сЗ и с4 во время компиляции неизвестны (исходя лишь нз информации в данной единице компиляции; см. й9.1), для них требуется выделить память. Адрес переменной с2 вычисляется (и где-то, наверное, используется), и для нее память выделять тоже нужно. Самым простым и часто встречающимся случаем является ситуация, когда значение константы во время компиляции известно и память под нее не выделяется; с1 служит примером такого случая. Ключевое слово ехтегп указывает, что переменная с4определена где-то в другом месте программы (э9.2).