1 00:00:00,000 --> 00:00:02,440 [Powered by Google Translate] [Pekare] 2 00:00:02,440 --> 00:00:05,000 [Rob Bowden] [Harvard University] 3 00:00:05,000 --> 00:00:07,360 [Detta är CS50] [CS50.TV] 4 00:00:07,360 --> 00:00:08,820 >> Låt oss tala om pekare. 5 00:00:08,820 --> 00:00:13,710 Hittills har vi alltid bara hänvisas till saker i minnet uttryckligen med namn. 6 00:00:13,710 --> 00:00:22,610 Vi har sagt int n = 42, och sedan när vi vill använda variabeln n, 7 00:00:22,610 --> 00:00:30,640 vi kallar det bara vid namn vi ger det genom att skriva något i stil med N * 2. 8 00:00:30,640 --> 00:00:34,790 Men den variabeln måste leva någonstans i minnet. 9 00:00:34,790 --> 00:00:37,790 När du vill använda det värde som för närvarande lagras inuti N, 10 00:00:37,790 --> 00:00:40,730 eller uppdatera värdet att n håller, 11 00:00:40,730 --> 00:00:44,180 ditt program behöver veta var i minnet för att leta efter n.. 12 00:00:44,180 --> 00:00:48,320 Var i minnet en variabel liv kallas dess adress. 13 00:00:48,320 --> 00:00:49,940 Det är som ett hus adress. 14 00:00:49,940 --> 00:00:53,080 Jag kan hitta någons hus så länge jag vet att deras hemadress, 15 00:00:53,080 --> 00:00:58,110 och ett datorprogram kan hitta en variabel så länge den känner dess minnesadress. 16 00:00:58,110 --> 00:01:02,660 Vilka pekare ger är ett sätt att direkt ta itu med dessa minnesadresser. 17 00:01:02,660 --> 00:01:06,860 >> En hel del av kraften i C kommer från att kunna manipulera minnet så här. 18 00:01:06,860 --> 00:01:09,960 Men med stor makt kommer stort ansvar. 19 00:01:09,960 --> 00:01:14,670 Pekare kan användas farligt nog att en hel del programmeringsspråk 20 00:01:14,670 --> 00:01:16,460 dölja pekare helt. 21 00:01:16,460 --> 00:01:19,440 Så, varför C ger oss pekare då? 22 00:01:19,440 --> 00:01:22,680 Som ni vet argument till en C-funktion 23 00:01:22,680 --> 00:01:25,370 kopieras alltid in parametrarna för funktionen. 24 00:01:25,370 --> 00:01:33,260 Så ringer något liknande swap på vissa variabler x och y 25 00:01:33,260 --> 00:01:38,840 kan inte byta värdena för x och y i den anropande funktionen, 26 00:01:38,840 --> 00:01:40,810 även om det kan vara praktiskt. 27 00:01:40,810 --> 00:01:46,430 Som vi kommer att se senare, skriva swap ta pekare till de platser som behöver bytas 28 00:01:46,430 --> 00:01:49,690 gör det möjligt att påverka dess uppringarens variabler. 29 00:01:49,690 --> 00:01:54,150 >> Låt oss gå igenom en relativt enkel exempel på vad pekare kan göra. 30 00:01:54,150 --> 00:02:15,550 Låt oss säga att vi har int n = 4, och int * pointer_to_n = & n. 31 00:02:15,550 --> 00:02:18,990 Whoa! Lite ny syntax för att täcka. 32 00:02:18,990 --> 00:02:22,600 Först, låt oss tolka detta & n. 33 00:02:22,600 --> 00:02:27,260 Kom ihåg att allt i minnet har någon adress. 34 00:02:27,260 --> 00:02:30,800 Et-tecknet kallas "adress" operatör. 35 00:02:30,800 --> 00:02:36,470 Så hänvisar & n till adressen i minnet där n lagras. 36 00:02:36,470 --> 00:02:41,560 Nu är vi lagrar denna adress i en ny variabel, pointer_to_n. 37 00:02:41,560 --> 00:02:43,870 Vilken typ av denna nya variabel? 38 00:02:43,870 --> 00:02:47,410 Asterisken är en del av variabelns typ, 39 00:02:47,410 --> 00:02:49,880 och vi läser den typ som int *. 40 00:02:49,880 --> 00:02:56,500 Int * betyder att pointer_to_n är en variabel som lagrar adressen för ett heltal. 41 00:02:56,500 --> 00:03:02,970 Vi vet att & n är en int * eftersom n är ett heltal, och vi tar adressen till n. 42 00:03:02,970 --> 00:03:06,660 Int * är ett exempel på en pekare typ. 43 00:03:06,660 --> 00:03:10,150 Så snart du börjar se asterisker i typ, 44 00:03:10,150 --> 00:03:11,950 du vet att du har att göra med pekare. 45 00:03:11,950 --> 00:03:16,520 Precis som vi kan deklarera en variabel som int x och röding y, 46 00:03:16,520 --> 00:03:20,410 Vi kan säga int * z och char * w. 47 00:03:20,410 --> 00:03:25,190 Int * och char * är bara nya typer för oss att använda. 48 00:03:25,190 --> 00:03:29,430 Placeringen av * kan gå någonstans innan variabelnamnet. 49 00:03:29,430 --> 00:03:34,730 Så både int * pointer_to_n - med * bredvid int, som vi har här - 50 00:03:34,730 --> 00:03:45,210 och int * pointer_to_n med * bredvid pointer_to_n är giltiga. 51 00:03:45,210 --> 00:03:56,470 Men här, jag placerar * bredvid int. 52 00:03:56,470 --> 00:04:00,600 Det spelar ingen roll vilken du föredrar, bara vara konsekvent. 53 00:04:00,600 --> 00:04:02,810 >> Låt oss rita ett diagram för detta. 54 00:04:02,810 --> 00:04:07,590 Vi har först variabeln n, som vi kommer att dra en liten ask minne. 55 00:04:07,590 --> 00:04:15,400 För detta exempel, låt oss säga att den här rutan ligger på adress 100. 56 00:04:15,400 --> 00:04:18,820 Inuti denna ruta, vi lagrar värdet 4. 57 00:04:18,820 --> 00:04:23,730 Nu har vi en ny variabel, pointer_to_n. 58 00:04:23,730 --> 00:04:27,030 Den har sin egen låda i minnet, 59 00:04:27,030 --> 00:04:32,900 som vi kommer att säga är adress 200. 60 00:04:32,900 --> 00:04:37,220 Inuti denna ruta, vi lagrar adressen till n, 61 00:04:37,220 --> 00:04:39,890 som vi sagt tidigare var 100. 62 00:04:39,890 --> 00:04:44,710 Ofta i diagram, ser du här visas som en bokstavlig pil 63 00:04:44,710 --> 00:04:48,730 lämnar pointer_to_n rutan pekar på rutan som lagrar n. 64 00:04:48,730 --> 00:04:54,620 Nu, vad kan vi faktiskt göra med pointer_to_n? 65 00:04:54,620 --> 00:05:10,400 Tja, om vi säger något i stil med * pointer_to_n = 8, är detta en annan användning för asterisk 66 00:05:10,400 --> 00:05:14,830 som är helt separat från användningen av asterisken i förklara en variabel 67 00:05:14,830 --> 00:05:16,790 av en pekare typ. 68 00:05:16,790 --> 00:05:21,130 Här är asterisken kallas dereference operatör. 69 00:05:21,130 --> 00:05:26,860 I vårt diagram, vad * pointer_to_n = 8 betyder är, 70 00:05:26,860 --> 00:05:32,220 Gå till rutan som innehåller pointer_to_n, följ pilen, 71 00:05:32,220 --> 00:05:38,160 och sedan tilldela rutan i slutet av pilen värdet 8. 72 00:05:38,160 --> 00:05:45,960 Detta innebär att efter denna linje, om vi försöker använda n kommer att ha värdet 8. 73 00:05:45,960 --> 00:05:51,600 Ordet "pekare" används i en mängd olika sammanhang. 74 00:05:51,600 --> 00:05:54,380 Här kommer vi försöka vara konsekvent. 75 00:05:54,380 --> 00:05:58,330 En pekare typ är något som int *. 76 00:05:58,330 --> 00:06:04,630 I den här videon kommer en pekare endast användas för att betyda ett värde med en pekare typ, 77 00:06:04,630 --> 00:06:08,180 Liksom pointer_to_n som har * typen int. 78 00:06:08,180 --> 00:06:15,140 Någonstans vi brukade bara säga n kan vi nu istället säga * pointer_to_n, 79 00:06:15,140 --> 00:06:18,020 och allt kommer att fungera lika bra. 80 00:06:18,020 --> 00:06:21,120 >> Låt oss gå igenom en annan enkelt exempel. 81 00:06:21,120 --> 00:06:50,390 Låt oss säga att vi har int n = 14, int * pekare = nanoteknik n + +, och (* pekare) + +. 82 00:06:50,390 --> 00:06:59,830 Den första raden skapas en ny ruta i minnet märkt N. 83 00:06:59,830 --> 00:07:05,400 Den här gången kommer vi märka inte lådan med en uttrycklig adress, men den har fortfarande en. 84 00:07:05,400 --> 00:07:11,810 Inuti lådan, vi lagrar numret 14. 85 00:07:11,810 --> 00:07:22,290 Nästa rad skapar en andra rutan pekare. 86 00:07:22,290 --> 00:07:27,210 Och insidan av denna ruta, vi lagrar en pekare till rutan n. 87 00:07:27,210 --> 00:07:33,170 Så, låt oss dra pilen från pekaren till n. 88 00:07:33,170 --> 00:07:37,790 Nu, n + + ökar värdet i rutan n, 89 00:07:37,790 --> 00:07:45,420 så vi går från 14 till 15. 90 00:07:45,420 --> 00:07:53,330 Slutligen (* pekare) + + går till rutan pekaren, 91 00:07:53,330 --> 00:08:02,660 dereferences värdet i rutan, vilket innebär att följa pilen till där det pekar, 92 00:08:02,660 --> 00:08:11,690 och ökar värdet lagras där, så vi går från 15 till 16. 93 00:08:11,690 --> 00:08:13,480 Och det är det. 94 00:08:13,480 --> 00:08:18,480 N lagrar nu den 16 efter att ha ökats två gånger - 95 00:08:18,480 --> 00:08:25,050 gång direkt med variabelnamnet n, och den andra genom en pointer_to_n. 96 00:08:25,050 --> 00:08:33,360 >> Snabb frågesport. Vad tror du det innebär om jag försöker säga något i stil med && n? 97 00:08:33,360 --> 00:08:41,350 Nåväl, låt oss skriva detta som & (& N), som åstadkommer samma sak. 98 00:08:41,350 --> 00:08:47,030 Den (& N) returnerar adressen för variabeln n i minnet. 99 00:08:47,030 --> 00:08:53,110 Men sedan då yttre-tecken försöker returnera adressen till adressen. 100 00:08:53,110 --> 00:08:56,600 Det är som att försöka göra och 2. 101 00:08:56,600 --> 00:09:00,550 Det är inte meningsfullt att få adressen till några nummer 102 00:09:00,550 --> 00:09:03,260 eftersom det inte är lagrat i minnet. 103 00:09:03,260 --> 00:09:07,090 Med hjälp av två et-tecken i rad är aldrig rätt idé. 104 00:09:07,090 --> 00:09:28,960 Men nu, vad betyder det om jag försöker säga int ** double_pointer = & pekare? 105 00:09:28,960 --> 00:09:40,750 Nu är jag skapar en ny låda märkt double_pointer, 106 00:09:40,750 --> 00:09:44,590 och insidan av den låda jag lagra adress pekaren, 107 00:09:44,590 --> 00:09:50,810 vilket betyder att jag drar en pil från double_pointer rutan till pekaren rutan. 108 00:09:50,810 --> 00:09:56,640 Lägg märke till typen av double_pointer, en int **. 109 00:09:56,640 --> 00:10:03,700 N var ett heltal, pekare lagrad adress n och så har * typen int. 110 00:10:03,700 --> 00:10:10,550 Nu lagrar double_pointer adress pekaren, så det har typen int. ** 111 00:10:10,550 --> 00:10:15,070 >> Så vad vi tror att detta innebär - 112 00:10:15,070 --> 00:10:24,490 ** Double_pointer = 23? 113 00:10:24,490 --> 00:10:28,630 Observera att jag nu är dereferencing två gånger. 114 00:10:28,630 --> 00:10:32,030 Följ bara rutan-och pil diagram vi redan inrättats. 115 00:10:32,030 --> 00:10:36,400 Först går vi till rutan double_pointer. 116 00:10:36,400 --> 00:10:40,550 Den första * betyder följer pilen gång. 117 00:10:40,550 --> 00:10:44,110 Nu är vi på rutan pekaren. 118 00:10:44,110 --> 00:10:49,940 Den andra stjärnan säger följer pilen igen, 119 00:10:49,940 --> 00:10:58,230 och nu är vi på rutan n och vi sätter värde rutan för att 23. 120 00:10:58,230 --> 00:11:05,940 Lägg märke till att dereference och "adress" operatörer inverser av varandra. 121 00:11:05,940 --> 00:11:16,990 Detta gör att jag kan göra något liknande * & * & n = 42. 122 00:11:16,990 --> 00:11:22,550 Medan detta fungerar ska du aldrig göra något liknande i praktiken. 123 00:11:22,550 --> 00:11:24,840 Vad gör vi egentligen här? 124 00:11:24,840 --> 00:11:28,700 Den första-tecken griper adress variabeln n. 125 00:11:28,700 --> 00:11:34,660 Sedan har vi en dereference operatör, vilket innebär att vi kommer till den adressen i minnet, 126 00:11:34,660 --> 00:11:36,910 så vi är tillbaka på n.. 127 00:11:36,910 --> 00:11:40,910 Nu, ta vi adressen till n igen och omedelbart dereference, 128 00:11:40,910 --> 00:11:50,780 så vi är tillbaka på n och lagra 42. 129 00:11:50,780 --> 00:11:55,490 Så varje par av * & bara upphäver. 130 00:11:55,490 --> 00:11:59,980 >> Det finns en speciell pekare som kallas NULL-pekare. 131 00:11:59,980 --> 00:12:03,140 Detta är en pekare som vi borde aldrig dereference. 132 00:12:03,140 --> 00:12:07,130 En sådan pekare är viktigt eftersom det ger oss ett sätt att skilja mellan 133 00:12:07,130 --> 00:12:10,220 en pekare som bör och inte bör dereferenced. 134 00:12:10,220 --> 00:12:13,050 Om du försöker dereference en nollpekare, 135 00:12:13,050 --> 00:12:17,150 vanligtvis ditt program kommer att krascha med ett segmentation fault, 136 00:12:17,150 --> 00:12:19,210 som du kan ha sett förut. 137 00:12:19,210 --> 00:12:30,490 Så, låt oss säga att vi har koden int * x = null; * x = 4. 138 00:12:30,490 --> 00:12:36,190 I detta exempel kan det tyckas självklart att vi gör något dåligt, 139 00:12:36,190 --> 00:12:40,650 men kom ihåg att null verkligen kan vara ett värde som returneras från ett anrop till en funktion 140 00:12:40,650 --> 00:12:45,930 Liksom malloc, är om malloc inte allokera det minne som begärs av användaren. 141 00:12:45,930 --> 00:12:50,200 Av denna anledning, om vi i stället ange värdet av x från ett samtal till malloc, 142 00:12:50,200 --> 00:13:12,050 som i int * x = malloc (sizeof (int)), bör vi alltid explicit kontrollera 143 00:13:12,050 --> 00:13:15,280 för att se om noll returnerades. 144 00:13:15,280 --> 00:13:43,250 Om (x == null) / / uhoh! avkastning, annars kan vi fortsätta och säga * x = 4. 145 00:13:43,250 --> 00:13:47,780 >> Så, återigen, varför skulle vi använda någonsin pekare? 146 00:13:47,780 --> 00:13:51,910 Låt oss titta på ett exempel på ett program där vi behöver använda pekare - 147 00:13:51,910 --> 00:13:54,110 en enkel swap funktion. 148 00:13:54,110 --> 00:14:08,270 Låt oss säga att jag har två heltal, int x = 4 och int y = 15; 149 00:14:08,270 --> 00:14:21,220 och jag vill skriva en funktion som kallas swap som jag kan använda som så: swap (x, y). 150 00:14:21,220 --> 00:14:28,270 Efter denna linje bör värdena insidan av variabeln x är 15, 151 00:14:28,270 --> 00:14:32,360 och värdet inuti variabel y skall vara 4. 152 00:14:32,360 --> 00:14:36,510 Värdena inuti x och y har bytts. 153 00:14:36,510 --> 00:14:53,040 Utan pekare, kan vi prova något liknande void swap (int a, int b); 154 00:14:53,040 --> 00:15:09,750 int tmp = b, b = a, a = tmp. 155 00:15:09,750 --> 00:15:12,960 Men märker du problemet med detta? 156 00:15:12,960 --> 00:15:19,000 Kom ihåg att det lagrade värdet i variabeln a är bara en kopia av värdet av x, 157 00:15:19,000 --> 00:15:22,000 och värdet i b kopieras från y. 158 00:15:22,000 --> 00:15:28,000 Alla ändringar som görs i a och b inte kommer att återspeglas i x-och y. 159 00:15:28,000 --> 00:15:32,050 Så medan värdena för a och b är korrekt swappas, 160 00:15:32,050 --> 00:15:35,810 x och y har inte ändras alls. 161 00:15:35,810 --> 00:15:38,480 Nu ska vi ändra swap-funktionen så att dess argument 162 00:15:38,480 --> 00:15:42,180 är pekare till de variabler bör Pendla, så här: 163 00:15:42,180 --> 00:15:56,880 void swap (int * a, int * b); int tmp = * b, * b = * a, * a = tmp. 164 00:15:56,880 --> 00:16:00,140 Kom ihåg att swappar argument nu pekare, 165 00:16:00,140 --> 00:16:05,670 så vi behöver för att klara adress x och y i samtalet att byta, så här: 166 00:16:05,670 --> 00:16:15,280 swap (& x, och y). 167 00:16:15,280 --> 00:16:20,520 Detta nu korrekt swappar värdena för x och y. 168 00:16:20,520 --> 00:16:24,310 >> Låt oss rita en ruta-och pil diagrammet för att se varför detta fungerar. 169 00:16:24,310 --> 00:16:28,520 Vi börjar med våra två lådor i minnet, x och y. 170 00:16:28,520 --> 00:16:35,780 Inuti lådan för x är antalet 4 och insidan av lådan för y vi 15. 171 00:16:35,780 --> 00:16:41,200 Nu, inne i anropet till swap-funktionen har vi två lådor 172 00:16:41,200 --> 00:16:45,140 för argumenten a och b; 173 00:16:45,140 --> 00:16:50,960 en pekar på rutan för x och b pekar på rutan för y. 174 00:16:50,960 --> 00:16:58,070 En ny ruta skapas för variabeln tmp, 175 00:16:58,070 --> 00:17:01,470 och insidan av det vi lagrar resultatet av dereferencing b, 176 00:17:01,470 --> 00:17:04,980 som betyder "följer pilen från rutan b.." 177 00:17:04,980 --> 00:17:09,880 Så lagrar vi 15 inuti tmp. 178 00:17:09,880 --> 00:17:20,560 Sedan följer vi pilen på b och lagra Här är resultatet av dereferencing en, 179 00:17:20,560 --> 00:17:24,569 vilken är värdet 4. 180 00:17:24,569 --> 00:17:35,590 Slutligen följer vi pilen på och lagra vad som för närvarande inne i TMP, vilket är 15. 181 00:17:35,590 --> 00:17:42,440 Observera att rutorna x och y korrekt bytt värden. 182 00:17:42,440 --> 00:17:46,290 >> När vi lär oss mer om malloc och dynamisk minneshantering, 183 00:17:46,290 --> 00:17:49,610 Vi kommer att se att vi inte har något annat val än att använda pekare. 184 00:17:49,610 --> 00:17:52,690 Vandring genom rutan-och pil diagram för alla program 185 00:17:52,690 --> 00:17:55,980 kan hjälpa dig att räkna ut vad programmet egentligen gör. 186 00:17:55,980 --> 00:17:59,680 >> Mitt namn är Rob Bowden, och detta är CS50. 187 00:18:00,000 --> 00:18:02,500 [CS50.TV] 188 00:18:02,500 --> 00:18:06,070 >> Detta är en annan användning för asterisk - bleah, jag hatar det ordet. 189 00:18:06,070 --> 00:18:13,960 Någonstans vi brukade bara säga N, kan vi säga nu pointer_to_n - nej du Värre - * pointer_to_n.