[Powered by Google Translate] [Показивачи]
[Роб Бовден] [Универзитет Харвард]
[Ово је ЦС50] [ЦС50.ТВ]
>> Хајде да причамо о показивача.
До сада смо увек само из стварима у меморији изричито имену.
Рекли смо инт н = 42, и онда када желимо да користимо променљиву н,
ми смо само га зову по имену смо га дати пишући нешто Н * 2.
Али, то мора да живи негде променљива у меморији.
Када желите да користите вредност која је тренутно ускладиштена у н,
или ажурирате вредност која н се држи,
Ваш програм треба да зна где у меморији да траже н.
Где у меморији променљивој живи зове се њена адреса.
То је као кућну адресу.
Ја могу наћи нечију кућу док ја знам њихову кућну адресу,
и компјутерски програм може да нађе променљиву док она зна своју меморијску адресу.
Шта показивачи дају је начин директно баве овим меморијским адресама.
>> Много снаге у Ц долази од могућности да манипулише меморију овако.
Али, са великом снагом долази и велика одговорност.
Показивачи се може користити опасно довољно да много програмских језика
сакрити показиваче у потпуности.
Па, зашто нам укаже Ц онда?
Као што знате, аргументи у Ц функцији
увек копирају у параметрима функције.
Дакле, називајући нешто свап на неким променљивих Кс и И
Не могу да петља вредности кии у функцији позива,
иако би то могао бити при руци.
Као што ћемо видети касније, преписивање своп да показивачи на локације којима је потребна да се замени
омогућава да утиче на променљиве свог саговорника.
>> Прошетајмо кроз релативно једноставан пример онога показивачи могу да ураде.
Рецимо имамо инт н = 4 и инт * поинтер_то_н = & н.
Вау! Мало нову синтаксу за покривање.
Прво, хајде да тумачите & н.
Сетите се да је све у меморији има неку адресу.
Амперсендом се зове "адреса" оператера.
Дакле, & Н се односи на адресу у меморији где се налазе н.
Сада смо складиштење ову адресу у новој променљивој, поинтер_то_н.
Шта је тип ове нове променљиве?
Звездица је део типа варијабле,
а ми ћемо читати као тип инт *.
Инт * значи да поинтер_то_н је променљива која складишти адресу цео број.
Знамо да је и н инт * јер је н цео број, а водимо адресу н.
Међ * је пример показивач типа.
Чим почнете да видите звездицу у врсти,
Ви знате да имате посла са тројке.
Као што можемо прогласити променљиву као инт и цхар и,
можемо рећи инт * З и знак * ж.
Инт * и цхар су само нове врсте за нас да користимо.
Положај * могу нигде да оде пре него што име променљиве.
Дакле, и инт * поинтер_то_н - са * поред ИНТ, јер ми овде имамо -
и инт * поинтер_то_н са * поред поинтер_то_н важе.
Али овде, ја ћу поставити * поред Инт.
Није битно који волите, само будите доследни.
>> Хајде да нацртате дијаграм за ово.
Ми прво морамо променљиву н, што ћемо извући као малу кутију меморије.
За овај пример, рецимо да је ово поље се налази на адреси 100.
Унутар овог оквира, ми чување вредности 4.
Сада имамо нову променљиву поинтер_то_н.
Она има своју кутију у меморији,
што ћемо рећи је на адреси 200.
Унутар овог оквира, ми смо складиштење адресу н,
који смо раније рекао да је 100.
Често у дијаграмима, видећете ово показало као дословном стрелице
остављајући поинтер_то_н кутија указујући на поље које складишти н.
Дакле, шта можемо урадити са поинтер_то_н заправо?
Па, ако кажемо нешто слично * поинтер_то_н = 8, ово је другачија употреба за звездицу
која је потпуно одвојена од коришћења звездицом у проглашења променљиву
од показивача типа.
Овде звездицу назива се дереференце оператера.
У нашем дијаграму, оно * поинтер_то_н = 8 значи,
идите на поље садржи поинтер_то_н, следите стрелицу,
а затим доделити поље на крају стрелице вредност 8.
То значи да после ове линије, ако покушамо да користимо н она ће имати вредност 8.
Реч "показивач" се користи у многим различитим контекстима.
Овде ћемо покушати да буде доследна.
Показивач тип је нешто као инт *.
У овом видеу, показивач ће се користити само да значи вредност са показивачем типа,
као поинтер_то_н који има типа инт *.
Било где смо навикли да само кажем н, сада можемо рећи, уместо * поинтер_то_н,
и све ће радити као добро.
>> Прошетајмо кроз другу једноставан пример.
Рецимо имамо инт н = 14; инт * показивац = &н; н + + и (* поентер) + +.
Прва линија креира нову кутију у меморији са ознаком Н.
Овај пут, нећемо обележи кутију са експлицитним адресу, али још увек има.
Унутар кутије, ми складиштење број 14.
Следећа линија ствара другу кутију са ознаком показивач.
И унутар тог оквира, ми складиштење показивач на поље са ознаком Н.
Дакле, хајде да нацртате стрелицу од казаљке на н.
Сада, н + + увећава вредност у пољу са ознаком н
па идемо од 14 до 15 година.
Коначно, (* поинтер) + + иде у кутији са ознаком показивача,
дереференцес вредност у пољу, што значи пратити стрелицу где поена,
и повећава вредност ускладиштена тамо, па идемо од 15 до 16 година.
И то је то.
Није сада чува број 16, након што је два пута увећава -
некада директно помоћу име променљиве н, а други кроз поинтер_то_н.
>> Брзо квиз. Шта мислиш да значи да ако покушам да кажем нешто слично && н?
Па, хајде да преправи ово као и (& Н) који остварује исту ствар.
Је (& Н) враћа адресу променљиве Н у меморији.
Али онда онда спољни амперсанд покушава да врати адресу адресу.
То је као да покушавате да урадите и 2.
То нема смисла да добије адресу само одредјени број
јер га не чувају у меморији.
Коришћење два амперсандс заредом није никада праву идеју.
Али сада, шта значи ако покушам да кажем инт ** доубле_поинтер = & показивач?
Сада сам стварајући нову кутију означену доубле_поинтер,
и унутар тог оквира сам складиштење адресу показивача,
што значи да извучемо једну стрелицу из доубле_поинтер кутије на показивач кутији.
Обратите пажњу на тип доубле_поинтер, инт **.
Није био цео, показивач складишти адресу н, па има типа инт *.
Сада, доубле_поинтер складишти адресу показивача, тако да има тип инт **.
>> Па, шта мислимо, то значи -
** Доубле_поинтер = 23?
Приметићете да сам сада ја два пута дереференцинг.
Само пратите бокс и стрелице дијаграм смо већ поставили.
Прво, идемо у поље са ознаком доубле_поинтер.
Први * значи следити једном стрелицу.
Сада смо у биоскопима са ознаком показивача.
Друга звезда каже да прати стрелицу,
и сада смо на табели са ознаком Н, а ми смо поставили вредност овог поља на 23 године.
Обратите пажњу да су дереференце и 'адреса' оператера су инверзни једне другима.
То ми омогућава да радим нешто слично * & * & н = 42.
Док ово ради, никада не треба да урадите нешто овако у пракси.
Шта ми заправо радимо овде?
Први амперсанд хвата адресу променљиве н.
Затим, имамо дереференце оператера, што значи да ће на ту адресу у меморији,
тако да смо се вратили на н.
Сада смо зграби адресу н поново и одмах дереференце,
тако да смо се вратили у н и продавница 42.
Дакле, сваки пар * & једноставно поништава.
>> Постоји посебан показивач зове нулл показивач.
Ово је показивач да ми никада требало дереференце.
Таква Показивач је важно јер нам пружа начин да направе разлику између
Показивач који треба и не треба дереференцед.
Ако покушате да дереференце нулл показивач,
обично је ваш програм ће се срушити са сегментације квара,
које сте можда видели раније.
Дакле, хајде да кажемо да имамо код инт * к = нулл; * к = 4.
У овом примеру, може изгледати очигледно да радимо нешто лоше,
али запамтите да ништаван бити стварно вредност вратио из позива на функцији
као маллоц, ако маллоц није у могућности да издвоји меморију тражене од стране корисника.
Из тог разлога, ако се уместо тога смо поставили вредност к од позива да маллоц,
као у инт * к = маллоц (сизеоф (инт)), онда увек треба експлицитно провери
да видим да ли нула враћена.
Иф (к == нулл); / / ухох! повратак елсе можемо наставити даље и кажу * к = 4.
>> Дакле, опет, зашто би смо икада користити показиваче?
Погледајмо пример програма, где морамо да користимо показиваче -
једноставна свап функција.
Рецимо да имате два цела броја, инт к = 4 и инт и = 15;
и желим да напишем неку функцију зове своп да могу да користе овако: свап (к, и).
После овој линији, вредности унутар променљиве к треба да буде 15,
и вредност у променљиву и треба да буде 4.
Вредности унутар од к и и су заменили.
Без тројке, можда ћемо покушати нешто слично воид свап (инт, инт б);
инт тмп = б, б =; = тмп.
Али, да ли сте приметили проблем са овим?
Запамтите да вредност чува у променљивој је само копија вредности к,
и вредност у БиХ се копира са и.
Све промене извршене на А и Б неће се одразити на кии.
Дакле, док су вредности А и Б су правилно замењен
Кс и И нису променили уопште.
Сада, хајде да промените функцију свап, тако да њени аргументи
су показивачи на променљиве треба свап, овако:
воид свап (инт *, инт * б); инт тмп = * б, б * = *, * = тмп.
Запамтите да свопови аргументи су сада показивачи,
па ми треба да прође адресу кии у позиву да мењају, овако:
свап (& к, & и).
Ово је сада исправно свопови вредностима к и и.
>> Хајде да нацртате оквир и стрелице дијаграм да видим зашто се ово ради.
Ми смо започели са нашим две кутије у меморији, Кс и И.
Унутар кутије за к је број 4, а унутар оквира за и имамо 15.
Сада, унутар позива на замену функције, имамо још две кутије
за аргументе и б;
а указује на кутији за к, а Б указује на кутији за и.
Нова кутија је направљен за променљиве тмп,
и унутар њега чувамо резултат дереференцинг б,
што значи "прати стрелицу из кутије са ознаком Б".
Дакле, чувамо 15 унутрашњост тмп.
Затим пратимо стрелицу на б и складиште овде резултат дереференцинг,
која је вредност 4.
Коначно, пратимо стрелицу у продавници и оно што је тренутно унутар тмп, који је 15.
Обратите пажњу да су кутије са ознаком Кс и И правилно замењен вредности.
>> Када смо сазнали више о маллоц и динамичан управљање меморијом,
ћемо видети да ми немамо избора него да користе показиваче.
Шетајући кутија и стрелицом дијаграму за било који програм
може да вам помогне да схватите шта програм заправо ради.
>> Моје име је Роб Бовден, а ово је ЦС50.
[ЦС50.ТВ]
>> Ово је другачија употреба за звездицом - блеах, мрзим ту реч.
Било где смо навикли да само кажем н, сада можемо рећи поинтер_то_н - не можете Зар не - * поинтер_то_н.