Г. Шилдт - Полный справочник по C++ (1109478), страница 23
Текст из файла (страница 23)
1п"); рг1пе1("Вы будете играть с компьютером.~п"); попе = 1пвс ваегьх(); бо( «)1 ар ваегух ( ) Глава 4. Массивы я строки де(: р1ауех. вохе() з бопе = с?зев()з /+ Есть ли победитель? / 11(депе!= ' ') ?згеаЕз /* Победитель определен!*/ дев соврцкег почв()! йопе = с?зес)!(]з /* Есть ли победитель? "/ ю?з11е(с?оде== ' '); 11(боле=='Х') рг1пку("Вы победили! 1п")! е1ае рг1пг1("Я выиграл(??!'зп")з 01вр васг1х()! /* Финавьное положЕнИЕ */ ге?цгп Оз ) /* Инициализация матрицы.
/ чоы 1п1с вагг1х(чо?б) ( 1пг з., ?ог(1=0! х<3: хе+) гог(3=0? 3<3? З.з.+) папг1х[1][3) /* Ход игрока. */ чо16 дес р].ауег воде(чо16) ( 1пг х, у! рг1по?("Введите координаты Х,У! ")з есап1("Ъба*саб", дх, ду]! 11(васк?х[х] [у) (= ' ') ( рг1пху("Неверный ход, попытайтесь епе. 1п") дев р1ауег пкче()! е1ве пахг1х[х] (у] = 'Х'з ) /* Ход компьптера. */ чо?б дег соврцсег вече(чо?б) ( 1пг х 3! йог(1=0! х<Зз х+е)( 1ог(Э=О! 3<З! 3++) 11(папг?х[1][3]=ы' ') ?згеа)<з 11(лзакг1х[11[1]==' ') Ьгеа)с! ) И (х*3==9] рг1по?("Ничья1п"); ех1г(0)! ) е1ее паог1х(1][3) = '0'! /* Вывести матрицу на экран.
"/ Часть ]. Основы языка С++! подмюкестаа С ззоЫ 61ар кастах(чоЫ) апг (г=оз г<зз г+ ) ртзптй(" Вс ! ес ( <с ",пастах[с) [О], иастзх[Г)[1], татках [Г][2]); зг(г!=2) рг1пгг(" (п †-(---[ †-1п")з ) ртзпгг("1п") з ) /* Проверить, есть ли победитель. */ с)зат с)зес)<(коЫ) ( Ыг з; тот(1=0/ з<З/ аь+) /* Проверка строк */ ай(пзаггах[з.] [О) ==зпагт1х[1] [1] сй иатг1х[1][0)==пастах[1][2]) гетцпз иаттах[з][0] тот(1=0/ 1<3/ 1<+) /* Проверка столбцов */ зй(пагт1х[О][1)х=зьаггах[1][1) йй тактах[0][1)=аиаст1х[2)[1)) тесцтп пзатгзх[0][1) /* Проверка диагоналей */ зг(пзастах[0)[0]=ашагтах[1][1] пастах[1][1]==тагт1х[2][2)) гегцтп пастах[0] [О) з аг(пзаггах[0][2]е=иагт1х[1][1] са пзасгах[1][1)==пзагтах[2][0]) гегцтп пастах[0][2]з тегцтп Глава 4. Массивы и строки уществуют три причины, по которым невозможно написать хорошую программу С на языке С/С++ без использования указателей.
Во-первых, указатели позволяют функциям изменять свои аргументы Во-вторых, с помощью указателей осуществляется динамическое распределение памяти. И, в-третьих, указатели повышают эффективность многих процедур. Как мы увидим в дальнейшем, в языке С++ указатели обладают дополнительными преимуществами.
Указатели — одно из самых моцшых и, в то же время, самых опасных средств языка С/С++. Например, неинициализированные указатели (или указатели, содержащие неверные алреса) могут уничтожить операционную сне~ему компьютера. И, что еще хуже, неправильное использование указателей порождает ошибки, которые крайне трудно обнаружить. Поскольку указатели настолько важны и опасны, мы рассмотрим их весьма подробно.
~Й~ Что такое указатель Указатеяь (ро)пэег) — это переменная, в которой хранится адрес другого объекта (как правило, другой переменной). Например, если одна переменная содержит адрес другой переменной, говорят, что первая переменная ссылается (ропп) на вторую. Эта ситуация показана на рнс. 5. К )Ъс. 5 1. Одна лерелгенная ссылается на другую Указатели Переменная, хранящая адрес ячейки памяти, должна быть объявлена как указатель.
Объявление указателя состоит из имени базового типа„символа и имени переменной. Общая форма этого объявления такова. тна указателя "имя указателя; Здесь тил указателя означает базовый тип указателя. Им может быть любой допустимый тип. Часть й Основы иаьиа С++: подмтлквстио С Базовый тип указателя определяется типом переменной, на которую он может ссылаться.
С формальной ~очки зрения указатель любого типа может ссылаться на любое место в памяти. Однако операции адресной арифметики тесно связаны с базовым типом указателей, поэтому очень важно правильно их объявить. <Адресная арифметика обсуждается ниже.) ~)-:-."'.""': Операторы для работы с указателями Эти операторы обсуждались в главе 2. Рассмотрим их повнилбательнее. Как известно, существуют два специальных оператора для работы с указателями: оператор разыменования указателя * и оператор получения адреса в.
Оператор бх является унарным и возвращает адрес своего операнда. <Не забывайте: унарные операборы имеют только один операнд.) Например, оператор присваивания $ ш = асоипеб записывает в указательш адрес переменной соиле. Этот адрес относится к ячейке памяти, которую занимает переменная собше . Адрес и значение переменной никак не связаны друг с другом. Оператор в означает "адрес". Следовательно, предыдущий оператор присваивания можно прочитать так: "присвоить указателю ш алрес переменной поппе*'.
Чтобы глубже разобраться в механизме, лежащем в основе предьиущего оператора присваивания, предположим, по переменная соапе хранится в ячейке с номером 2000. Оператор разыменования указателя * является антиподом оператора в. Этот унарный оператор возвращает значение, хранящееся по указанному адресу. Например, если указатель ш содержит адрес переменной соппе, то оператор присваивания поместит значение сюппе в переменную ш Следовательно, переменная бг станет равной )00, поскольку именно это число записано в ячейке 2000, адрес которой хранится в указателе ш Символ * можно интерпретировать как "значение, храбьчшееся по алресу".
В данном случае предыдущий оператор означает: "присвоить переменной бх значение, хранящееся по адресу ш". Приоритет операторов в и * выше, чем приоритет всех арифметических операторов, за исктючением унарного мин>са. Необходимо иметь гаранзии„что указатель всегда ссылается на переменную правильного типа. Например, если в программе объявлен указатель на целочисленную переменную, компилятор полагает, что адрес, который в нем содержится, относится к переменной типа 1пе, независимо от того, так ли это на самом деле.
Поскольку указателю можно присвоить любой адрес, следующая программа будет скомпилирована без ошибок, но желаемого результата не даст. ()дпс1иб)е <эеб)1о.п> ъпс шаъп(уо1д) < б)оиЬ1е х = 100.1, у; Епе *р; /* Слелуюязбй оператор заставит указатель р (имеюпий целочисленный тип) ссылаться на переменную типа б)оцЬ1е. */ р = ахб б Если переменная занимает несколько ячеек памяти, ее адресом считается адрес первой ячейки. — Прим.
лед. Глава б. Указатели /* Следукззий оператор работает нв так, как задумано. */ у = *р' рг1пег("вг", у); /* Не выводит число 100.1 */ гегикв 0; ) Эта программа никогда не присвоит переменной у значение псременной и. Поскольку указатель р является целочисленным, в переменную у будут скопированы лишь 4 байт (поскольку целые числа занимают 4 байт), а не 3. рявйДЯЕу~Щ$ и языке С++ нельзя изменить тип указателя, нв используя явное приведение ЩЩ~ЩЩЩИ~ типов.
В языке С приввовнив типов используется в большинство случаев. ~:~ Выражения, содержащие указатели Как правило, выражения, содержащие указатели, подчиняются обн)епринятым правилам. Однако у ник есть свои особенности. Присваивание указателей Указатель можно присваивать другому указателю. Рассмотрим пример. Фзгс1ибе <вгбго.)з> 1пе йп( И) 1пс х; тпс *р1, *рг; р1 = ах; р2 = р1; ргзпсз(" Ър". р2): /* Выводит адрес переменной х, а не ее значение! "/ гесикп 0; ) Теперь на переменную х ссылаются оба указателя рз и рг.
Аарес переменной и выводится на экран с помощью ()юрматного спецификатора ър и функции ркавес () . Адреснав арифметика К указателям можно применять ~олько две арифметические операции: сложение и вычитание. Предположим, что указатель р1 ссылается на целочисленную переменную, размещенную по адресу 2000, Кроме того, будем считать„что целые числа занимают 2 байт в памяти компьютера.
После вычисления выражения 6 р1+ переменная рз будет равна не 200), а 2002, поскольку при увеличении указателя р1 на единицу он ссылается на следующее целое число. Это же относится и к оператору декрементации. Например, если указатель р1 равен 2000, выражение Часть!. Основы языка С++( подмножество С р1 присвоит указатсшо р1 значение 199В. Обобщая привсдепныи выше пример, сформулируем следующие правила адресной арифметики. При увеличении указатель ссылается на ячейку, в которой хранится следующий элемент базового типа. При уменьшении он ссьшается на предыдущий элемент. Для указателеи на символы сохраняются правила "обычнои" арифметики, поскольку размер символов раасн 1 байт.
Вес оста~виме указатели увеличиваются или уменьшаются на длину соответствующих переменных, на которые они ссылаются. Зто гарантирует, что указатели все~да будут ссылаться на элемент базового типа. Описанная выше концепция проилшострирована на рис. 5.2. сЬс. 52, Адресная арифметика тесно связана с базовым типом (предполагается, что длина иелочисленной переменной равна двум байтал|) Деиствия над указателями не ограничиваются операторами инкрементации и дскрсментации. Например. к ним можно добавлять и целые числа. Выражение В р1 = р1 т 12; смешает указатель р1 на 12-й элемент базового типа, расположенный после текущего адреса.