246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 34
Текст из файла (страница 34)
Кроме того, в ряде случаев нельзя однозначно ответить на вопрос онеобходимости применения указателей. На этом занятии последовательно, шаг за шагом, выосвоитеосновныепринципыработысуказателями.Однакоосознатьвсюмощьэтихсредстввысможете,толькопрочитавкнигудоконца.ЧтотакоеуказательУказатель—этопеременная,вкоторойзаписанадресячейкипамятикомпьютера.Чтобыпонять,какработаютуказатели,необходимохотябывобщихчертах,ознакомитьсясбазовыми принципами организации машинной памяти.
Машинная память состоит изпоследовательности пронумерованных ячеек. Значение каждой переменной хранится вотдельной ячейке памяти, которая называется ее адресом. На рис. 8.1 изображена структураразмещениявпамятичетырехбайтовогоцелогозначенияпеременнойtheAge.Дляразныхкомпьютеровхарактерныразличныеправилаадресациипамяти,имеющиесвоиособенности. Однако в большинстве случаев программисту не обязательно знать точный адрескакой-либо переменной — эту задачу выполняет компьютер. При необходимости такуюинформацию можно получить с помощью оператора адреса (&).
Пример использования этогооператораприведенвлистинге8.1.Рис.8.1.СохранениевпамятипеременнойtheAgeЛистинг8.1.Операторадреса1://Листинг8.1.Примериспользования2://оператораадреса3:4:#include<iostream.h>5:6:intmain()7:{8:unsignedshortshortVar=5;9:unsignedlonglongVar=65535;10:longsVar=-65535;11:12:cout<<"shortVar:\t"<<shortVar;13:cout<<"AddressofshortVar:\t";14:cout<<&shortVar<<"\n";15:16:cout<<"longVar:\t"<<longVar;17:cout<<"AddressoflongVar:\t"18:cout<<&longVar<<"\n";19:20:cout<<"s.Var:\t"<<sVar;21:cout<<"AddressofsVar:\t"22:cout<<&sVar<<"\n";23:24:return0;25:}Результат:shortVar:5AddressofshortVar:0x8fc9:fff4longVar:65535AddressoflongVar:0x8fc9:fff2sVar:-65535AddressofsVar:0x8fc9:ffee(Ваширезультатымогутотличатьсяотприведенныхвлистинге.)Анализ:Вначалепрограммыобъявляютсяиинициализируютсятрипеременные:встроке8—переменнаятипаunsignedshort,встроке9—типаunsignedlong,австроке10—типаlong.Затем в строках 12-16 выводятся значения и адреса этих переменных, полученные с помощьюоператораадреса(&).Призапускепрограммынакомпьютереспроцессором80386значениепеременнойshortVarравно 5, а ее адрес — 0x8fc9:fff4.
Адрес размещения переменной выбирается компьютером иможет изменяться при каждом последующем запуске программы. Поэтому ваши результатымогут отличаться от приведенных. Причем разница между двумя первыми адресами будетоставаться постоянной. При двухбайтовом представлении типа short эта разница составит 2байта, а разница между третьим и четвертым адресами — 4 байта при четырехбайтовомпредставлениитипаlong.Порядокразмещенияэтихпеременныхвпамятипоказаннарис.8.2.В большинстве случаев вам не придется непосредственно манипулировать адресамипеременных. Важно лишь знать, какой объем памяти занимает переменная и как получить ееадрес в случае необходимости. Программист лишь указывает компилятору объем памяти,доступный для размещения статических переменных, после чего размещение переменной поопределенному адресу будет выполняться автоматически. Обычно тип long имеетчетырехбайтовое представление.
Это означает, что для хранения переменной этого типапотребуетсячетыребайтамашиннойпамяти.ИспользованиеуказателякаксредствахраненияадресаКаждая переменная программы имеет свой адрес, для хранения которого можноиспользоватьуказательнаэтупеременную.Причемсамозначениеадресазнатьнеобязательно.Допустим, что переменная howOld имеет тип int.
Чтобы объявить указатель pAge дляхраненияадресаэтойпеременной,наберитеследующийфрагменткода:int*pAge=0;Этой строкой переменная pAge объявляется указателем на тип int. Это означает, что pAgeбудетсодержатьадресзначениятипаint.Отметим, что pAge ничем не отличается от любой другой переменной. При объявлениипеременнойцелочисленноготипа(например,int)мыуказываемнато,чтовнейбудетхранитьсяцелоечисло.Когдажепеременнаяобъявляетсяуказателемнакакой-либотип,этоозначает,чтоонабудетхранитьадреспеременнойданноготипа.Такимобразом,указателиявляютсяпростоотдельнымтипомпеременных.В данном примере переменная pAge инициализируется нулевым значением. Указатели,значениякоторыхравны0,называютпустыми.Послеобъявленияуказателюобязательнодолжноприсваиваться какое-либо значение.
Если заранее неизвестно, какой адрес должен храниться вуказателе,емуприсваиваетсязначение0.Неинициализированныеуказателивдальнейшеммогутстатьпричинойбольшихнеприятностей.Поскольку при объявлении указателю pAge было присвоено значение 0, далее ему нужноприсвоитьадрескакой-либопеременной,напримерhowOld.Какэтосделать,показанониже:unsignedshortinthowOld=50;//объявляемпеременнуюunsignedshortint*pAge=0;//объявляемуказательpAge=&howOld;//ПрисвоениеуказателюpAgeадресапеременнойhowOldРис.8.2.СхемасохраненияпеременнойвпамятиВ первой строке объявлена переменная howOld типа unsigned short int и ей присвоенозначение 50. Во второй строке объявлен указатель pAge на тип unsigned short int, которомуприсвоенозначение0.Символ"звездочка"(*),стоящийпосленаименованиятипа,указываетнато,чтоописаннаяпеременнаяявляетсяуказателем.В последней строке указателю pAge присваивается адрес переменной howOld.
На этоуказываетоператорадреса(&)передименемпеременнойhowOld.Еслибыэтогооператоранебыло, присваивался бы не адрес, а значение переменной, которое также может являтьсякорректнымадресом.В нашем случае значением указателя pAge будет адрес переменной howOld, значениекоторой равно 50. Две последние строки рассмотренного фрагмента программы можнообъединитьводну:unsignedshortinthowOld=50;//объявляемпеременнуюunsignedshortint*pAge=&how01d;//объявляемуказательнапеременнуюhowOldТеперь указатель pAge содержит адрес переменной howOld. С помощью этого указателяможно получить и значение переменной, на которую он указывает. В нашем примере этозначение равно 50. Обращение к значению how01d посредством указателя pAge называетсяоперацией разыменования или косвенного обращения, поскольку осуществляется неявноеобращениекпеременнойhow01d,адрескоторойсодержитсявуказателе.Далеевыузнаете,какспомощьюразыменовываниявозвращатьзначенияпеременных.Косвенное обращение подразумевает получение значения переменной, адрес которойсодержитсявуказателе,аоператорразыменованияпозволяетизвлечьэтозначение.ИменауказателейПоскольку указатели являются обычными переменными, называть их можно любымикорректными для переменных именами.
Для выделения указателей среди других переменныхмногие программисты используют перед их именами символ "p" (от англ. pointer), напримерpAgeилиpNumber.ОператорразыменовыванияОператор косвенного обращения (или оператор разыменования) позволяет получитьзначение,хранящеесяпоадресу,записанномувуказателе.В отличие от указателя, при обращении к обычной переменной осуществляется доступнепосредственно к ее значению. Например, чтобы объявить новую переменную типа unsignedshortint,азатемприсвоитьейзначениедругойпеременной,можнонаписатьследующее:unsignedshortintyourAge;yourAge=howOld;Прикосвенномдоступебудетполученозначение,хранящеесяпоуказанномуадресу.ЧтобыприсвоитьновойпеременнойyourAgeзначениеhow01d,используяуказательpAge,содержащийееадрес,напишитеследующее:unsignedshortintyourAge;yourAge=*pAge;Операторразыменования(*)передпеременнойpAgeможетрассматриватьсякак"значение,хранящееся по адресу".
Таким образом, вся операция присваивания означает: "получитьзначение,хранящеесяпоадресу,записанномувpAge,иприсвоитьегопеременнойyourAge".Примечание:Оператор разыменования можно использовать с указателями двумяразнымиспособами:дляобъявленияуказателяидляегоразыменовывания.Вслучаеобъявленияуказателя символ звездочки сигнализирует компилятору, что это не простая переменная, ауказатель,например:unsignedshort<<pAge=0;//объявляетсяуказатель//напеременнуютипаunsignedshortВ случае разыменовывания указателя символ звездочки означает, что операция должнапроизводиться не над самим адресом, а над значением, сохраненным по адресу, которыйхранитсявуказателе:*pAge=5;//присваиваетзначение5переменнойпоадресувуказателеpAgeТакженепутайтеоператорразыменовываниясоператоромумножения(*).Компиляторпоконтекстуопределяет,какойименнооператориспользуетсявданномслучае.Указатели,адресаипеременныеЧтобы овладеть навыками программирования на C++, вам в первую очередь необходимопонимать, в чем различие между указателем, адресом, хранящимся в указателе, и значением,записаннымпоадресу,хранящемусявуказателе.Впротивномслучаеэтоможетпривестикрядусерьезныхошибокпринаписаниипрограмм.Рассмотримещеодинфрагментпрограммы:inttheVariable=5;int*pPointer=&theVariable;В первой строке объявляется переменная целого типа theVariable.
Затем ей присваиваетсязначение5.Вследующейстрокеобъявляетсяуказательнатипint,которомуприсваиваетсяадреспеременнойtheVariable.ПеременнаяpPointerявляетсяуказателемисодержитадреспеременнойtheVariable. Значение, хранящееся по адресу, записанному в pPointer, равно 5. На рис. 8.3схематическипоказанаструктураэтихпеременных.Рис.8.3.СхемараспределенияпамятиОбращениекданнымчерезуказателиПослетогокакуказателюприсвоенадрескакой-либопеременной,егоможноиспользоватьдля работы со значением этой переменной.
В листинге 8.2 показан пример обращения кзначениюлокальнойпеременнойчерезуказательнанее.Листинг8.2.Обращениекданнымчерезуказатели1://Листинг8.2.Использованиеуказателей2:3:#include<iostream.h>4:5:typedefunsignedshortintUSHORT;6:intmain()7:{8:USHORTmyAge;//переменная9:USHORT*pAge=0;//указатель10:myAge=5;11:cout<<"myAge:"<<myAge<<"\n";12:pAge=&myAge;//заносимвpAgeадрзсmyAge13:cout<<"*pAge:"<<*pAge<<"\n\n";14:cout<<"*pAge=7\n";15:*pAge=7;//присваиваемmyAgeзначение716:cout<<"*pAge:"<<*pAge<<"\n";17:cout<<"myAge:"<<myAge<<"\n\n";18:cout<<"myAge=9\n";19:myAge=9;20:cout<<"myAge:"<<myAge<<"\n";21:cout<<"*pAge:"<<*pAge<<"\n";22:23:return0;24:}Результат:myAge:5*pAge:5*pAge:=7*pAge:7myAge:7myAge=9myAge:9*pAge:9Анализ: В программе объявлены две переменные: myAge типа unsigned short и pAge,являющаясяуказателемнаэтоттип.Встроке10переменнойpAgeприсваиваетсязначение5,австроке11этозначениевыводитсянаэкран.Затем в строке 12 указателю pAge присваивается адрес переменной myAge.