Г. Шилдт - С#4.0 Полное руководство (1160795), страница 133
Текст из файла (страница 133)
Таким образом, приведенное выше присваивание словами можно описать так: "Переменная зр получает адрес переменной пшп." 684 Часть (. язык С(г Второй оператор, *, является дополнением оператора ь. Этот унарный оператор находит значение переменной, расположенной по адресу, на который указывает его операнд. Следовательно, этот оператор обращается к значению переменной, на которую указывает соответствующий указатель. Так, если переменная 1р содержит адрес памяти переменной пипч как показано в предыдущем примере, то в следующей строке кода: ьпс ча1 = *1р; в переменной ча1 сохраняется значение 10 переменной пиль на которую указывает переменная зр.
Операцию * можно рассматривать как получение значения по адресу. Поэтому приведенный выше оператор присваивания описывается словами следующим образом: "Переменная ча1 получает значение по адресу, хранящемуся в переменной 1р." Оператор * можно использовать также в левой части оператора присваивания. В этом случае он задает значение, на которое указывает соответствующий указатель, как в приведенном ниже примере. *1р = 100; В данном примере значение 100 присваивается переменной, на которую указывает переменная Тр, т.е. переменной пппь Поэтому приведенный выше оператор присваивания описывается словами следующим образом: "Разместить значение 100 по адресу, хранящемуся в переменной 1р." Применение ключевого слова ипеайе Любой код, в котором используются указатели, должен быть помечен как небезопасный с помощью специального ключевого слова ппзате.
Подобным образом можно пометить конкретные типы данных (например, классы и структуры), члены класса (в том числе методы и операторы) или отдельные кодовые блоки как небезопасные. В качестве примера ниже приведена программа, где указатели используются в методе Маьп (), помеченном как небезопасный.
О Продемонстрировать применение указателей и ключевого слова ппяате. ияьлч Буятаю1 с1аяя Ппяатесоое ( Пометить метод Ма1п() как небезопасный. опяате ягагьс чоха Ма1п() ( ьпс соопс = 99; ьпс* р; // создать указатель типа 1пс р = асочпш // поместить адрес переменной попас в переменной р сопяо1е.хг1сеььпе("исходное значение переменной соплю " + *р); *р = 10; !/ присвоить значение 10 переменной сопля, /Г на которую указывает переменная р Сопяо1е.игьпевьпе("Новое значение переменной сопля: " т *р) Глава 20. Небезопасный код, указатели, обнуляемые типы и разные ключевые слова 685 Эта программа дает следующий результат.
Исходное значение переменной соппг: 99 Новое значение переменной соппг: 10 Применение модификатора шсей В работе с указателями нередко используется модификатор бхеб, который препятствует удалению управляемой переменной средствами "сборки мусора". Потребность в этом возникает, например, в том случае, если указатель обращается к полю в объекте определенного класса. А поскольку указателю ничего не извесгно о действиях системы "сборки мусора", то он будет указывать не на тот объект, если удалить нужный объект. Ниже приведена общая форма модификатора ((хес(: 11хеб (тип* р = Яфиксированньят объект) ( // использовать фиксированный объект ) где р обозначает указатель, которому присваивается адрес объекта.
Этот объект будет оставаться на своем текущем месте в памяти до конца выполнения кодового блока. В качестве адресата оператора ((хег( может быть также указано единственное выражение, а не целый кодовый блок. Модификатор 1(хес( допускается использовать только в коде, помеченном как небезопасный. Кроме того, несколько указателей с модификатором ((хег( могут быть объявлены списком через запятую. Ниже приведен пример применения модификатора бхес(. О Продемонстрировать применение оператора 11хеб. пятно Яуясещ; с1аяя Теяс ( рпбгъс ьпг ппщч рпщъс Теяг(1пс ъ) ( ппщ = 1; ) с1аяв Гьхеособе Пометить метод Маъп() как небезопасный. опяате ягапьс чоьб Ма1п() ( Теяп о = пеы Теяг(19); уьхеб (ьпс* р = ьо.ппщ) ( !/ использовать модификатор уьхеб дпя размещения адреса переменной экземпляр о.ппщ в переменной р Сопяо1е.нтчвеъъпе("Исходное значение переменной о.ппщ: " + *р); *р = 10; /! присвоить значение 10 переменной соппс, на которую указывает переменная р Сопяо1е.нг11е01пе("Новое значение переменной о.ппщ: " + *р); ) Вот к какому результату приводит выполнение этой программы.
686 часть!. язык С() Исходное значение переменной о.ппсп 19 Новое значение переменной о.ппм: 10 В данном.примере модификатор 1(хе() препятствует удалению объекта о. А поскольку переменная р указывает на переменную экземпляра о. пппь то она будет указывать на недостоверную область памяти, если удалить объект о.
Доступ к членам структуры с помощью указателя Указатель может указывать на объект типа структуры при условии, что структура не содержит ссылочные типы данных. Для доступа к члену структуры с помощью указателя следует использовать оператор-стрелку (->), а не оператор-точку (.). Например, доступ к членам структуры вггосС Музгсист ( роЫ1с 1пС а; роЫ1с 1пс Ь; рпЫ1с 1пС Янм() ( гегпгп а + Ь; ) осуществляется следующим образом. МузбсооС о = лен МуЯСгисС()( МузоспсС* р; // объявить указатель р = ао( р->а = 10; // использовать оператор -> р->Ь = 20; // использовать оператор -> Сопзо1е.кгьтеъппе("Сумма равна " + р->япм()); Арифметические операции над указателями Над указателями можно выполнять только четыре арифметические операпии: ++, --, т и —.
Для того чтобы стало понятнее, что именно происходит в арифметических операциях над указателями, рассмотрим сначала простой пример. Допустим, что переменная р1 является указателем с текущим значением 2000, т.е. она содержит адрес 2000. После выполнения выражения р)-~-; переменная р1 будет содержать значение 2004, а не 2001! Дело в том, что после каждого инкрементирования переменная р1 указывает на следующее значение типа ьпс. А поскольку тип ьпС представлен в С)) 4 байтами, то в результате инкрементирования значение переменной р1 увеличивается на 4. Справедливо и обратное; при каждом декрементировании переменной р1 ее значение уменьшается на 4. Например выражение р) —; приводит к тому, что значение переменной р1 становится равным 1996, если раньше оно было равно 2000! Все сказанное выше можно обобщить: после каждого инкрементирования указатель будет указывать на область памяти, где хранится следующий элемент его соотносимого типа, а после каждого декрементирования указатель будет указывать на область памяти, где хранится предыдущий элемент его соотносимого типа.
Глава 20. Небезопасный код, указатели, обнуляеыые типы и разные ключевые слова 887 Арифметические операции над указателями не ограничиваются только инкрементированием и декрементированием. К указателям можно добавлять и вычитать из них целые значения. Так, после вычисления следующего выражения: р1 = р1 + 9; переменная р1 будет указывать на девятый элемент ее соотносимого типа по отношению к элементу, на который она указывает в настоящий момент. Если складывать указатели нельзя, то разрешается вычитать один указатель из другого, при условии, что оба указателя имеют один и тот же соотносимый тип. Результатом такой операции окажется количество элементов соотносимого типа, которые разделяют оба указателя.
Кроме сложения и вычитания целого числа из указателя, а также вычитания двух указателей, другие арифметические операции над указателями не разрешаются. В частности, к указателям нельзя добавлять или вычитать из них значения типа ()оас или с)опЫе. Не допускаются также арифметические операции над указателями типа мосс)*.
Для того чтобы проверить на практике результаты арифметических операций над указателями, выполните приведенную ниже короткую программу, где выводятся физические адреса, на которые указывает целочисленный указатель (1р) и указатель с плавающей точкой одинарной точности (гр). Понаблюдайте за изменениями каждого из этих указателей по отношению к их соотносимым типам на каждом шаге цикла.
// Продемонстрировать результаты арифметических операций иад указателями. ця1пч зуяоепц с1аяя РСслсьСЬПеио ( цпяаге яоаозс чозп Маги() ( 1пС х( 1пс 1; с(оцЬ1е О( гпс* зр = 4(.; ОоцЬ1е* Гр = ьс(; ОооЬ1е1п") Сопяо1е.исгоесспе("спС бог(х=О; х < 10; х++) ( Сопяо1е.нсьпеььпе ( (ц1пС) (1р) + " " + (цспС) (ГР) ) ' гртт) ГР~-7 ) Ниже приведен примерный результат выполнения данной программы. У вас он может оказаться иным, хотя промежутки между выводимыми значения должны быть такими же самыми. ьпС ОоцЬ1е 1243464 1243468 1243468 1243476 1243472 1243484 1243476 1243492 1243480 1243500 688 Часть ].
Язык С]4 1243484 1243508 1243488 1243516 1243492 1243524 1243498 1243532 1243500 1243540 Как следует из приведенного выше результата, арифметические операции выполняются над указателями относительно их соотносимого типа. Так, значения типа 1пс занимают в памяти 4 байта, а значения типа г)сц)>1е — В байтов, и поэтому их адреса изменяются с приращением именно на эти величины. Сравнение указателей Указатели можно сравнивать с помощью таких операторов отношения, как ==, < и >.
Но для того чтобы результат сравнения указателей оказался содержательным, оба указателя должны быть каким-то образом связаны друг с другом. Так, если переменные р1 и р2 являются указателями на две разные и не связанные вместе переменные, то любое их сравнение, как правило, не имеет никакого смысла.
Но если переменные р1 и р2 указывают на связанные вместе переменные, например на элементы одного массива, то их сравнение может иметь определенный смысл. указатели и массивы В СФ указатели и массивы связаны друг с другом. Например, при указании имени массива без индекса в операторе с модификатором бхеб формируется указатель на начало массива. В качестве примера рассмотрим следующую программу. /* Указание имени массива без индекса приводит к формированию указателя на начало массива. */ сяьлч зуягеяи с1аяя Ргглггау ] спеете яяавьс тета Ма1л]) ] тля)] лиюя = лен тлг]10]; Гьхеб(1ля* р = ялсюя)0], р2 = лсюя) 11)р == рг) Ссляс1е.нгтаевьле )"Указатели р и р2 содержат "один и тот же адрес."); ) ) ) Ниже приведен результат выполнения этой программы.