1 00:00:00,000 --> 00:00:02,440 [Powered by Google Translate] [Показалки] 2 00:00:02,440 --> 00:00:05,000 [Роб Bowden] [Харвардския университет] 3 00:00:05,000 --> 00:00:07,360 [Това е CS50 [CS50.TV] 4 00:00:07,360 --> 00:00:08,820 >> Да поговорим за указатели. 5 00:00:08,820 --> 00:00:13,710 До момента, ние винаги сме само към нещата в паметта изрично от името. 6 00:00:13,710 --> 00:00:22,610 Казахме INT N = 42 и тогава, когато искаме да използваме променливата N, 7 00:00:22,610 --> 00:00:30,640 ние просто го наричат ​​с името му даваме от писането на нещо като N * 2. 8 00:00:30,640 --> 00:00:34,790 Но тази променлива трябва да живеят някъде в паметта. 9 00:00:34,790 --> 00:00:37,790 Когато искате да използвате стойност, която в момента се съхраняват във вътрешността н 10 00:00:37,790 --> 00:00:40,730 или актуализиране на стойността, която N държи, 11 00:00:40,730 --> 00:00:44,180 вашата програма трябва да знаете къде в паметта, за да търсят N. 12 00:00:44,180 --> 00:00:48,320 Когато в паметта с променлива живота се нарича неговия адрес. 13 00:00:48,320 --> 00:00:49,940 Това е като къща адрес. 14 00:00:49,940 --> 00:00:53,080 Мога да намеря къщата на някого толкова дълго, колкото знам, домашния си адрес, 15 00:00:53,080 --> 00:00:58,110 и компютърна програма, може да се намери променлива толкова дълго, колкото го знае нейната адресна памет. 16 00:00:58,110 --> 00:01:02,660 Какво указатели осигуряват е начин пряко се занимават с тези адреси от паметта. 17 00:01:02,660 --> 00:01:06,860 >> Много от властта в C не е в състояние да манипулират памет като тази. 18 00:01:06,860 --> 00:01:09,960 Но с голяма сила идва и голяма отговорност. 19 00:01:09,960 --> 00:01:14,670 Указатели може да се използва опасно достатъчно, че много езици за програмиране 20 00:01:14,670 --> 00:01:16,460 скрий указатели изцяло. 21 00:01:16,460 --> 00:01:19,440 Така че, защо C ни даде насоки? 22 00:01:19,440 --> 00:01:22,680 Както знаете, аргументи за функция C 23 00:01:22,680 --> 00:01:25,370 винаги са копирани в параметрите на функцията. 24 00:01:25,370 --> 00:01:33,260 Така че, наричайки нещо като суап на някои променливи х и у 25 00:01:33,260 --> 00:01:38,840 не могат да разменят стойности на х и у в викащата функция, 26 00:01:38,840 --> 00:01:40,810 въпреки че може да е полезно. 27 00:01:40,810 --> 00:01:46,430 Както ще видим по-късно, пренаписване разменяне на указатели към местата, които трябва да бъдат сменени 28 00:01:46,430 --> 00:01:49,690 позволява да засегне променливите си абонати. 29 00:01:49,690 --> 00:01:54,150 >> Нека минем през сравнително прост пример за това какво може да направи указатели. 30 00:01:54,150 --> 00:02:15,550 Нека кажем, че имаме INT N = 4 и вътр * pointer_to_n = & N. 31 00:02:15,550 --> 00:02:18,990 Уау! Малко нов синтаксис за покриване. 32 00:02:18,990 --> 00:02:22,600 Първо, нека да тълкуват това и N. 33 00:02:22,600 --> 00:02:27,260 Не забравяйте, че всичко в паметта има някакъв адрес. 34 00:02:27,260 --> 00:02:30,800 Амперсанд се нарича "адрес на" оператор. 35 00:02:30,800 --> 00:02:36,470 Така че, се отнася до адреса в паметта, където се съхранява N & N. 36 00:02:36,470 --> 00:02:41,560 Сега, ние сме съхраняване на този адрес в нова променлива, pointer_to_n. 37 00:02:41,560 --> 00:02:43,870 Какъв е типът на тази нова променлива? 38 00:02:43,870 --> 00:02:47,410 Звездичката е част от типа на променливата, 39 00:02:47,410 --> 00:02:49,880 и ние ще прочетете типа, INT *. 40 00:02:49,880 --> 00:02:56,500 Int * означава, че pointer_to_n е променлива, съдържаща адрес на цяло число. 41 00:02:56,500 --> 00:03:02,970 Ние знаем, че & N е INT *, тъй като N е цяло число, и ние сме като адреса на N. 42 00:03:02,970 --> 00:03:06,660 Int * е пример за типа указател. 43 00:03:06,660 --> 00:03:10,150 Веднага след като започнете да виждате звездички във вида, 44 00:03:10,150 --> 00:03:11,950 знаете, че си имате работа с указатели. 45 00:03:11,950 --> 00:03:16,520 Точно както можем да декларираме променлива INT х и Чар г., 46 00:03:16,520 --> 00:03:20,410 можем да кажем, вътр * Z и Чар * Ш. 47 00:03:20,410 --> 00:03:25,190 Int * и Чар * са само нови видове за нас да използваме. 48 00:03:25,190 --> 00:03:29,430 Мястото на * пред името на променливата може да отиде навсякъде. 49 00:03:29,430 --> 00:03:34,730 Така че, както Int * pointer_to_n - с * до INT, тъй като имаме тук - 50 00:03:34,730 --> 00:03:45,210 и вътр * pointer_to_n с * до pointer_to_n са валидни. 51 00:03:45,210 --> 00:03:56,470 Но тук, аз ще поставим * до Int. 52 00:03:56,470 --> 00:04:00,600 Няма значение кое предпочитате, просто да бъде последователна. 53 00:04:00,600 --> 00:04:02,810 >> Да се ​​изготви схема за това. 54 00:04:02,810 --> 00:04:07,590 Първия има променлива N, който ще привлече като малка кутийка с памет. 55 00:04:07,590 --> 00:04:15,400 За този пример, нека да кажем, че това поле се намира на адрес 100. 56 00:04:15,400 --> 00:04:18,820 Вътре в това поле, ние сме съхраняване на стойност 4. 57 00:04:18,820 --> 00:04:23,730 Сега имаме нова променлива, pointer_to_n. 58 00:04:23,730 --> 00:04:27,030 Разполага със собствен прозорец в паметта, 59 00:04:27,030 --> 00:04:32,900 което ще кажем, че е на адрес 200. 60 00:04:32,900 --> 00:04:37,220 Вътре в това поле, ние сме съхраняване на адреса на N, 61 00:04:37,220 --> 00:04:39,890 които ние преди това каза, че е 100. 62 00:04:39,890 --> 00:04:44,710 Често в диаграми, ще видите, това е показано като буквално стрелка 63 00:04:44,710 --> 00:04:48,730 оставяйки pointer_to_n кутия сочи към полето, което се съхранява н. 64 00:04:48,730 --> 00:04:54,620 Сега, това, което можем реално с pointer_to_n? 65 00:04:54,620 --> 00:05:10,400 Е, ако кажем нещо подобно * pointer_to_n = 8, това е различно за звездичка 66 00:05:10,400 --> 00:05:14,830 че е напълно отделен от използването на звездичка за обявяване на променлива 67 00:05:14,830 --> 00:05:16,790 от тип указател. 68 00:05:16,790 --> 00:05:21,130 Ето, звездичката се нарича сочен. 69 00:05:21,130 --> 00:05:26,860 В нашата диаграма, какво * pointer_to_n = 8 средства, 70 00:05:26,860 --> 00:05:32,220 кутия, съдържаща pointer_to_n, следвайте стрелката, 71 00:05:32,220 --> 00:05:38,160 и след това присвоите на полето в края на стрелката на стойност 8. 72 00:05:38,160 --> 00:05:45,960 Това означава, че след този ред, ако се опитваме да използваме N ще имат стойност 8. 73 00:05:45,960 --> 00:05:51,600 Думата "показалеца" се използва в много различни контексти. 74 00:05:51,600 --> 00:05:54,380 Ето, ние ще се опитаме да бъде последователна. 75 00:05:54,380 --> 00:05:58,330 А показалеца тип е нещо като INT *. 76 00:05:58,330 --> 00:06:04,630 В това видео, само ще се използва показалка означава стойност с показалеца тип, 77 00:06:04,630 --> 00:06:08,180 като pointer_to_n която има * тип Int. 78 00:06:08,180 --> 00:06:15,140 Навсякъде можем да кажем N, можем да сега вместо каже * pointer_to_n, 79 00:06:15,140 --> 00:06:18,020 и всичко ще работи също толкова добре. 80 00:06:18,020 --> 00:06:21,120 >> Нека разгледаме един прост пример. 81 00:06:21,120 --> 00:06:50,390 Нека кажем, че имаме INT N = 14, вътр * показалеца = &n; N + +, и (* показалеца) + +. 82 00:06:50,390 --> 00:06:59,830 Първият ред създава нова кутия в паметта етикетирани н. 83 00:06:59,830 --> 00:07:05,400 Този път, ние няма да етикетират кутия с изрично адрес, но тя все още има една. 84 00:07:05,400 --> 00:07:11,810 Вътрешността на кутията, ние сме съхраняване на номер 14. 85 00:07:11,810 --> 00:07:22,290 Следващият ред създава втори кутията с надпис показалеца. 86 00:07:22,290 --> 00:07:27,210 И в рамките на това поле, ние сме съхраняване на показалеца на кутията с надпис N. 87 00:07:27,210 --> 00:07:33,170 Така че, нека да привлече стрелката от показалеца, за да н. 88 00:07:33,170 --> 00:07:37,790 Сега, N + + нарастване на стойността в кутията с надпис N, 89 00:07:37,790 --> 00:07:45,420 така че ние 14-15. 90 00:07:45,420 --> 00:07:53,330 И накрая, (* показалеца) + + отива в кутията с надпис показалеца, 91 00:07:53,330 --> 00:08:02,660 dereferences стойност в полето, което означава, следвайте стрелката на къде сочи, 92 00:08:02,660 --> 00:08:11,690 и нарастване на стойността, съхранена там, така че отиваме 15-16. 93 00:08:11,690 --> 00:08:13,480 И това е всичко. 94 00:08:13,480 --> 00:08:18,480 N съхранява номер 16 след като е бил увеличен два пъти - 95 00:08:18,480 --> 00:08:25,050 веднъж директно с помощта на променливата N име, а други чрез pointer_to_n. 96 00:08:25,050 --> 00:08:33,360 >> Бърз тест. Какво мислиш, че означава, че ако аз се опитам да кажа нещо подобно && N? 97 00:08:33,360 --> 00:08:41,350 Ами, нека да пренапише това като & (н), който прави същото. 98 00:08:41,350 --> 00:08:47,030 (Н) връща адреса на променливата N в паметта. 99 00:08:47,030 --> 00:08:53,110 Но след това след това външната амперсанд се опитва да се върне на адрес на адрес. 100 00:08:53,110 --> 00:08:56,600 Това е като да се опитвам да направя и две. 101 00:08:56,600 --> 00:09:00,550 То няма смисъл да се получи на адрес само на известен брой 102 00:09:00,550 --> 00:09:03,260 тъй като той не се съхраняват в паметта. 103 00:09:03,260 --> 00:09:07,090 Използване на две амперсанди в ред никога не е на прав път. 104 00:09:07,090 --> 00:09:28,960 Но сега, какво означава това, ако се опитам да се каже, вътр ** double_pointer = показалеца? 105 00:09:28,960 --> 00:09:40,750 Сега, аз съм създаване на нова кутията с надпис double_pointer 106 00:09:40,750 --> 00:09:44,590 и вътре в тази кутия съм съхраняване на адреса на показалеца, 107 00:09:44,590 --> 00:09:50,810 , което означава, че начертайте стрелка от кутията double_pointer на показалеца кутия. 108 00:09:50,810 --> 00:09:56,640 Обърнете внимание на вида на double_pointer, INT **. 109 00:09:56,640 --> 00:10:03,700 N е цяло число, показалецът съхранява адреса на N, и затова има * тип Int. 110 00:10:03,700 --> 00:10:10,550 Сега, double_pointer съхранява адреса на показалеца, така че има тип Int **. 111 00:10:10,550 --> 00:10:15,070 >> Така че, това, което смятаме, че това означава - 112 00:10:15,070 --> 00:10:24,490 ** Double_pointer = 23? 113 00:10:24,490 --> 00:10:28,630 Забележете, че сега съм dereferencing два пъти. 114 00:10:28,630 --> 00:10:32,030 Просто следвайте полето и стрелката схема вече сме създадени. 115 00:10:32,030 --> 00:10:36,400 Първо, ние се връщаме към кутията с надпис double_pointer. 116 00:10:36,400 --> 00:10:40,550 Първо * означава, следвайте стрелката веднъж. 117 00:10:40,550 --> 00:10:44,110 Сега, ние сме в кутията с надпис показалеца. 118 00:10:44,110 --> 00:10:49,940 Втората звезда казва, че отново върху стрелката, 119 00:10:49,940 --> 00:10:58,230 и сега сме в кутията с надпис N, и стойността на тази клетка 23. 120 00:10:58,230 --> 00:11:05,940 Забележете, че сочен и адреса на "оператори са обратно пропорционални един на друг. 121 00:11:05,940 --> 00:11:16,990 Това ми позволява да правя нещо подобно * & * & N = 42. 122 00:11:16,990 --> 00:11:22,550 Докато това стане, вие никога не трябва да направя нещо подобно в практиката. 123 00:11:22,550 --> 00:11:24,840 Какво всъщност правиш тук? 124 00:11:24,840 --> 00:11:28,700 Първо амперсанд грабва адрес на променливата N. 125 00:11:28,700 --> 00:11:34,660 След това имаме и сочен оператор, което означава, че на този адрес в паметта, 126 00:11:34,660 --> 00:11:36,910 така че ние сме най-н. 127 00:11:36,910 --> 00:11:40,910 Сега, ние вземете адреса на N отново и веднага сочен 128 00:11:40,910 --> 00:11:50,780 така че ние сме най-н и магазин 42. 129 00:11:50,780 --> 00:11:55,490 Така че, всяка двойка * & отменя. 130 00:11:55,490 --> 00:11:59,980 >> Има специална показалеца, наречена нулев указател. 131 00:11:59,980 --> 00:12:03,140 Това е указател, че ние никога не бива сочен. 132 00:12:03,140 --> 00:12:07,130 Такава показалеца е важно, защото ни дава възможност да се направи разграничение между 133 00:12:07,130 --> 00:12:10,220 показалеца, които трябва и не трябва да се dereferenced. 134 00:12:10,220 --> 00:12:13,050 Ако се опитате да сочен с нулев указател, 135 00:12:13,050 --> 00:12:17,150 обикновено вашата програма ще се срине с сегментация вина 136 00:12:17,150 --> 00:12:19,210 които може да сте виждали преди. 137 00:12:19,210 --> 00:12:30,490 Така че, нека кажем, че имаме кода Int * х = NULL; * х = 4. 138 00:12:30,490 --> 00:12:36,190 В този пример, може да изглежда очевидно, че правим нещо лошо, 139 00:12:36,190 --> 00:12:40,650 но не забравяйте, че за нищожна наистина може да бъде стойността, върната от извикване на функция 140 00:12:40,650 --> 00:12:45,930 като изчистване, ако изчистване не е в състояние да се разпредели памет, поискани от потребителя. 141 00:12:45,930 --> 00:12:50,200 Поради тази причина, ако вместо стойността на х от извикването на изчистване, 142 00:12:50,200 --> 00:13:12,050 както и в Int * х = изчистване (sizeof (INT)), тогава ние трябва винаги изрично проверите 143 00:13:12,050 --> 00:13:15,280 за да видите дали нищожна е върнат. 144 00:13:15,280 --> 00:13:43,250 Ако (х == NULL) / / uhoh! връщане; друго, което може да продължи и да каже * х = 4. 145 00:13:43,250 --> 00:13:47,780 >> Така че, отново, защо трябва да се някога използват указатели? 146 00:13:47,780 --> 00:13:51,910 Нека разгледаме един пример на една програма, където ние трябва да използваме указатели - 147 00:13:51,910 --> 00:13:54,110 проста функция суап. 148 00:13:54,110 --> 00:14:08,270 Да кажем, че имам две цели числа, вътр х = 4 и Int Y = 15; 149 00:14:08,270 --> 00:14:21,220 и искам да напиша функция, наречена суап, които мога да използвам като така: суап (X, Y). 150 00:14:21,220 --> 00:14:28,270 След тази линия, стойностите вътре на променливата х трябва да бъде 15, 151 00:14:28,270 --> 00:14:32,360 и стойност вътре променлива г. трябва да бъде 4. 152 00:14:32,360 --> 00:14:36,510 Стойностите вътрешността на х и у са разменени. 153 00:14:36,510 --> 00:14:53,040 Без указатели, бихме могли да се опитаме нещо като невалидни суап (Int а, вътр б); 154 00:14:53,040 --> 00:15:09,750 Int TMP = б, б = а; а = ПТУ. 155 00:15:09,750 --> 00:15:12,960 Но, да забележите, че проблем с това? 156 00:15:12,960 --> 00:15:19,000 Не забравяйте, че стойността, съхранена в променливата а е просто копие на стойността на х, 157 00:15:19,000 --> 00:15:22,000 и стойност в б е копиран от години. 158 00:15:22,000 --> 00:15:28,000 Всички промени, направени в А и Б няма да бъдат отразени в х и у. 159 00:15:28,000 --> 00:15:32,050 Така че, докато стойностите на а и б са правилно разменят, 160 00:15:32,050 --> 00:15:35,810 х и у не са се променили изобщо. 161 00:15:35,810 --> 00:15:38,480 Сега, нека да промените функцията суап, така че аргументите си 162 00:15:38,480 --> 00:15:42,180 са указатели към променливите трябва да се сменят, като така: 163 00:15:42,180 --> 00:15:56,880 невалидни суап (INT * а, вътр * б); вътр TMP = * б * б = * А; * а = ПТУ. 164 00:15:56,880 --> 00:16:00,140 Не забравяйте, че суапове аргументи са указатели, 165 00:16:00,140 --> 00:16:05,670 и затова ние трябва да премине адреса на х и у в поканата да се разменят, като така: 166 00:16:05,670 --> 00:16:15,280 суап (& X, и г). 167 00:16:15,280 --> 00:16:20,520 Това сега правилно суапове стойности на х и у. 168 00:16:20,520 --> 00:16:24,310 >> Нека се направи кутия и стрелката диаграма, за да се види защо това работи. 169 00:16:24,310 --> 00:16:28,520 Ние започваме с нашите две кутии в паметта, х и у. 170 00:16:28,520 --> 00:16:35,780 Вътре в полето за х е номер 4, и вътрешността на кутия за години имаме 15. 171 00:16:35,780 --> 00:16:41,200 Сега, в рамките на поканата за суап функция, ние имаме още две кутии 172 00:16:41,200 --> 00:16:45,140 за аргументите А и Б; 173 00:16:45,140 --> 00:16:50,960 точкова полето за х, и б пункта до полето за години. 174 00:16:50,960 --> 00:16:58,070 Се създава нова кутия за променливата малки, 175 00:16:58,070 --> 00:17:01,470 и вътре в него ние съхраняваме резултат от dereferencing б, 176 00:17:01,470 --> 00:17:04,980 , което означава "стрела от кутията с надпис б." 177 00:17:04,980 --> 00:17:09,880 Така че, ние съхраняваме 15 вътрешността на ПТУ. 178 00:17:09,880 --> 00:17:20,560 След това, ние следваме стрелката в б и магазин у нас в резултат на dereferencing 179 00:17:20,560 --> 00:17:24,569 , който е на стойност 4. 180 00:17:24,569 --> 00:17:35,590 И накрая, ние следваме стрелката в това, което е в момента в рамките на малки и магазин, който е 15. 181 00:17:35,590 --> 00:17:42,440 Забележете, че квадратчетата х и у правилно са разменени стойности. 182 00:17:42,440 --> 00:17:46,290 >> След като научите повече за изчистване и динамично управление на паметта, 183 00:17:46,290 --> 00:17:49,610 ще видим, че имаме друг избор, освен да използват указатели. 184 00:17:49,610 --> 00:17:52,690 Разглеждане на през полето и стрелката схема за всяка програма 185 00:17:52,690 --> 00:17:55,980 може да ви помогне да разберете това, което програмата е наистина прави. 186 00:17:55,980 --> 00:17:59,680 >> Моето име е Роб Боудън, и това е CS50. 187 00:18:00,000 --> 00:18:02,500 [CS50.TV] 188 00:18:02,500 --> 00:18:06,070 >> Това е различно за звездичка - bleah, мразя тази дума. 189 00:18:06,070 --> 00:18:13,960 Навсякъде можем да кажем N, можем да се каже, pointer_to_n - не 'т - * pointer_to_n.