1 00:00:00,000 --> 00:00:02,440 [Powered by Google Translate] [Mutatók] 2 00:00:02,440 --> 00:00:05,000 [Rob Bowden] [Harvard Egyetem] 3 00:00:05,000 --> 00:00:07,360 [Ez CS50] [CS50.TV] 4 00:00:07,360 --> 00:00:08,820 >> Beszéljünk mutatók. 5 00:00:08,820 --> 00:00:13,710 Eddig mi mindig imént említett dolgok a memóriában kifejezetten név szerint. 6 00:00:13,710 --> 00:00:22,610 Már mondtam int n = 42, és akkor, amikor akarjuk használni a változó n, 7 00:00:22,610 --> 00:00:30,640 hívjuk azt a nevet adjuk, hogy írásban valami ilyesmi n * 2. 8 00:00:30,640 --> 00:00:34,790 De ez változót kell élni valahol a memóriában. 9 00:00:34,790 --> 00:00:37,790 Ha szeretné használni az érték jelenleg tárolt n, 10 00:00:37,790 --> 00:00:40,730 vagy frissíteni az az érték, n birtok, 11 00:00:40,730 --> 00:00:44,180 A programnak tudnia kell, hol a memóriában keresni n. 12 00:00:44,180 --> 00:00:48,320 Ha a memóriában a változó életet nevezzük annak címét. 13 00:00:48,320 --> 00:00:49,940 Ez olyan, mint egy ház címet. 14 00:00:49,940 --> 00:00:53,080 Találok valakit házába, amíg tudom, hogy a lakcím, 15 00:00:53,080 --> 00:00:58,110 és egy számítógépes program talál egy változó, amíg tudja a memória címét. 16 00:00:58,110 --> 00:01:02,660 Milyen mutatók nyújtanak egy módja, hogy közvetlenül foglalkozik ezekkel a memória címeket. 17 00:01:02,660 --> 00:01:06,860 >> Sok a hatalom C származik, hogy képes manipulálni memóriát, mint ez. 18 00:01:06,860 --> 00:01:09,960 De a nagy hatalommal nagy felelősség jár. 19 00:01:09,960 --> 00:01:14,670 Mutatók lehet használni veszélyesen ahhoz, hogy egy csomó programozási nyelvek 20 00:01:14,670 --> 00:01:16,460 elrejteni mutatók teljesen. 21 00:01:16,460 --> 00:01:19,440 Szóval, miért C nekünk mutató akkor? 22 00:01:19,440 --> 00:01:22,680 Mint tudod, érveket a C függvény 23 00:01:22,680 --> 00:01:25,370 mindig másolja a paramétereket a funkciót. 24 00:01:25,370 --> 00:01:33,260 Szóval, amelyben valami ilyesmit swap bizonyos változók x és y 25 00:01:33,260 --> 00:01:38,840 nem tudja cserélje fel az az x és y a hívó funkció, 26 00:01:38,840 --> 00:01:40,810 annak ellenére, hogy lehet, hogy praktikus. 27 00:01:40,810 --> 00:01:46,430 Mint látni fogjuk később, újraírás csere, hogy rámutatnak a helyekre kelljen cserélték 28 00:01:46,430 --> 00:01:49,690 lehetővé teszi, hogy befolyásolja a hívó változók. 29 00:01:49,690 --> 00:01:54,150 >> Sétáljunk egy viszonylag egyszerű példa arra, mi mutatók tehet. 30 00:01:54,150 --> 00:02:15,550 Tegyük fel, hogy van int n = 4, és int * pointer_to_n = & n. 31 00:02:15,550 --> 00:02:18,990 Hűha! Egy kicsit az új szintaxis fedezni. 32 00:02:18,990 --> 00:02:22,600 Először is, hadd értelmezi ezt a & n. 33 00:02:22,600 --> 00:02:27,260 Ne feledje, hogy minden a memóriában van néhány címet. 34 00:02:27,260 --> 00:02:30,800 A jel az úgynevezett "címe" operátor. 35 00:02:30,800 --> 00:02:36,470 Így, & n utal a cím memóriában ahol n tárolják. 36 00:02:36,470 --> 00:02:41,560 Most tárolja ezt a címet egy új változó, pointer_to_n. 37 00:02:41,560 --> 00:02:43,870 Milyen típusú az új változó? 38 00:02:43,870 --> 00:02:47,410 A csillag része a változó típusát, 39 00:02:47,410 --> 00:02:49,880 és mi olvasni a típus int *. 40 00:02:49,880 --> 00:02:56,500 Int * azt jelenti, hogy pointer_to_n olyan változó, amely tárolja a címét egy egész. 41 00:02:56,500 --> 00:03:02,970 Tudjuk, hogy a & n int * mivel n egész szám, és mi figyelembe a címét n. 42 00:03:02,970 --> 00:03:06,660 Int * egy példa egy mutató típus. 43 00:03:06,660 --> 00:03:10,150 Amint elkezdi látni csillagokat a típusát, 44 00:03:10,150 --> 00:03:11,950 tudod, hogy dolgunk mutatók. 45 00:03:11,950 --> 00:03:16,520 Csakúgy, mint mi is, hogy egy változót, mint int x és y char, 46 00:03:16,520 --> 00:03:20,410 azt mondhatjuk, int * z és char * w. 47 00:03:20,410 --> 00:03:25,190 Int * és char * csak új típusú nekünk használni. 48 00:03:25,190 --> 00:03:29,430 A helyszín a * mehet sehova, mielőtt a változó nevét. 49 00:03:29,430 --> 00:03:34,730 Tehát mindkét int * pointer_to_n - a * mellett int, hiszen van itt - 50 00:03:34,730 --> 00:03:45,210 és int * pointer_to_n a * mellett pointer_to_n érvényesek. 51 00:03:45,210 --> 00:03:56,470 De itt, én helyezze a * mellett int. 52 00:03:56,470 --> 00:04:00,600 Nem számít, amely szeretné, csak legyen következetes. 53 00:04:00,600 --> 00:04:02,810 >> Nézzünk felhívni a diagram erre. 54 00:04:02,810 --> 00:04:07,590 Először is a változó n, amit akkor döntetlen, mint egy kis doboz a memória. 55 00:04:07,590 --> 00:04:15,400 Ebben a példában, mondjuk, hogy ez a rovat található címen 100. 56 00:04:15,400 --> 00:04:18,820 Belül ezt a rovatot, mi tárolása érték 4. 57 00:04:18,820 --> 00:04:23,730 Most van egy új változót, pointer_to_n. 58 00:04:23,730 --> 00:04:27,030 Megvan a saját mezőbe a memóriában, 59 00:04:27,030 --> 00:04:32,900 amit majd mondani címen 200. 60 00:04:32,900 --> 00:04:37,220 Belül ezt a rovatot, mi tárolása címét n, 61 00:04:37,220 --> 00:04:39,890 amelyhez előtt azt mondta, hogy 100-zal. 62 00:04:39,890 --> 00:04:44,710 Gyakran a diagramok, látni fogod, ezt mutatja, mint egy szó nyíl 63 00:04:44,710 --> 00:04:48,730 elhagyó pointer_to_n dobozt mutat a doboz, amely tárolja n. 64 00:04:48,730 --> 00:04:54,620 Nos, mit tudunk valójában csinálni pointer_to_n? 65 00:04:54,620 --> 00:05:10,400 Nos, ha azt mondjuk, hogy valami hasonlót * pointer_to_n = 8, ez egy másik alkalmazása a csillag 66 00:05:10,400 --> 00:05:14,830 amely teljesen elkülönül a használata a csillag nyilvánító változó 67 00:05:14,830 --> 00:05:16,790 Egy pointer típusú. 68 00:05:16,790 --> 00:05:21,130 Itt a csillagot nevezzük dereference operátor. 69 00:05:21,130 --> 00:05:26,860 A mi diagram, amit * pointer_to_n = 8 azt jelenti, 70 00:05:26,860 --> 00:05:32,220 megy a dobozban pointer_to_n kövesse a nyilat, 71 00:05:32,220 --> 00:05:38,160 és utána hozzárendeli a végén lévő bekeretezett a nyíl a 8-at. 72 00:05:38,160 --> 00:05:45,960 Ez azt jelenti, hogy miután ezt a sort, ha megpróbáljuk használni n ez lesz az érték 8. 73 00:05:45,960 --> 00:05:51,600 A "pointer" kifejezés sok különböző helyzetekben. 74 00:05:51,600 --> 00:05:54,380 Itt megpróbáljuk lenniük. 75 00:05:54,380 --> 00:05:58,330 A mutató típus valami ilyesmi int *. 76 00:05:58,330 --> 00:06:04,630 Ebben a videóban a mutató csak akkor használható olyan érték egy mutató típusú, 77 00:06:04,630 --> 00:06:08,180 mint pointer_to_n amelynek típusa int *. 78 00:06:08,180 --> 00:06:15,140 Bárhol szoktuk mondjuk n, most már inkább azt mondják * pointer_to_n, 79 00:06:15,140 --> 00:06:18,020 és minden működik ugyanúgy. 80 00:06:18,020 --> 00:06:21,120 >> Sétáljunk egy másik egyszerű példát. 81 00:06:21,120 --> 00:06:50,390 Tegyük fel, hogy van int n = 14; int * pointer = &n; n + +, és (* pointer) + +. 82 00:06:50,390 --> 00:06:59,830 Az első sor létrehoz egy új rovat a memória feliratú n. 83 00:06:59,830 --> 00:07:05,400 Ezúttal nem címkézik a dobozt egy explicit címet, de még mindig van egy. 84 00:07:05,400 --> 00:07:11,810 Belül a doboz, mi tárolja a számot 14. 85 00:07:11,810 --> 00:07:22,290 A következő sorban létrehoz egy második doboz feliratú mutató. 86 00:07:22,290 --> 00:07:27,210 És belül ezt a rovatot, mi tárolása mutatót a mezőbe n. 87 00:07:27,210 --> 00:07:33,170 Szóval, most felhívni a nyilat mutató n. 88 00:07:33,170 --> 00:07:37,790 Most, n + + lépésenként az értéket a mezőbe n, 89 00:07:37,790 --> 00:07:45,420 így megy 14-15. 90 00:07:45,420 --> 00:07:53,330 Végül a (* pointer) + + megy a mezőbe mutató, 91 00:07:53,330 --> 00:08:02,660 dereferences az értéket a mezőbe, ami azt jelenti, kövesse a nyilat, ahol megjegyzi, 92 00:08:02,660 --> 00:08:11,690 és növeli az értéket az ott tárolt, így megy 15-16. 93 00:08:11,690 --> 00:08:13,480 És ennyi. 94 00:08:13,480 --> 00:08:18,480 N most tárolja a 16 szám, miután kétszer is növekszik - 95 00:08:18,480 --> 00:08:25,050 egyszer közvetlenül a változó nevét N, és a másik egy pointer_to_n. 96 00:08:25,050 --> 00:08:33,360 >> Gyors kvíz. Mit gondolsz, ez azt jelenti, ha megpróbálok mondani valamit, mint && n? 97 00:08:33,360 --> 00:08:41,350 Nos, akkor átírják ezt és (& n), amely megvalósítja az ugyanaz a dolog. 98 00:08:41,350 --> 00:08:47,030 A (+ n) visszaadja a címét n változó a memóriában. 99 00:08:47,030 --> 00:08:53,110 De akkor aztán külső jelet próbál visszatérni a cím a címet. 100 00:08:53,110 --> 00:08:56,600 Ez olyan, mintha próbálkozik és 2. 101 00:08:56,600 --> 00:09:00,550 Ennek semmi értelme, hogy a címét csak néhány szám 102 00:09:00,550 --> 00:09:03,260 mivel ez nem tárolja a memóriában. 103 00:09:03,260 --> 00:09:07,090 Két ampersands egy sorban soha nem egy jó ötlet. 104 00:09:07,090 --> 00:09:28,960 De most, mit jelent az, ha megpróbálom elmondani int ** double_pointer = & pointer? 105 00:09:28,960 --> 00:09:40,750 Most hozok létre egy új feliratú mezőben double_pointer, 106 00:09:40,750 --> 00:09:44,590 és belső e rovat vagyok tárolása címét mutató, 107 00:09:44,590 --> 00:09:50,810 ami azt jelenti, hogy dolgozzon egy nyilat a double_pointer mezőben a mutató mezőben. 108 00:09:50,810 --> 00:09:56,640 Figyeljük meg, hogy milyen típusú double_pointer, int **. 109 00:09:56,640 --> 00:10:03,700 N volt egy egész szám, mutató tárolt a címét, N, és így azt, típus int *. 110 00:10:03,700 --> 00:10:10,550 Most double_pointer tárolja címe mutató, így azt írja int **. 111 00:10:10,550 --> 00:10:15,070 >> Szóval, mit gondolunk ez azt jelenti - 112 00:10:15,070 --> 00:10:24,490 ** Double_pointer = 23? 113 00:10:24,490 --> 00:10:28,630 Figyeljük meg, hogy én vagyok most dereferencing kétszer. 114 00:10:28,630 --> 00:10:32,030 Csak kövesse a doboz-és nyíl diagram mi már létrehozott. 115 00:10:32,030 --> 00:10:36,400 Először megyünk a mezőbe double_pointer. 116 00:10:36,400 --> 00:10:40,550 * Az első azt jelenti, kövesse a nyilat egyszer. 117 00:10:40,550 --> 00:10:44,110 Most vagyunk a mezőbe mutatót. 118 00:10:44,110 --> 00:10:49,940 A második csillag azt mondja, kövesse a nyilat újra, 119 00:10:49,940 --> 00:10:58,230 és most mi vagyunk a mezőbe n, és állítsa az értékét doboz 23. 120 00:10:58,230 --> 00:11:05,940 Figyeljük meg, hogy a dereference és "címe" piaci szereplők inverze egymásnak. 121 00:11:05,940 --> 00:11:16,990 Ez lehetővé teszi, hogy tegyek valamit, mint a * & * & n = 42. 122 00:11:16,990 --> 00:11:22,550 Bár ez működik, akkor soha nem valami ilyesmi a gyakorlatban. 123 00:11:22,550 --> 00:11:24,840 Mit keresünk valójában csinál itt? 124 00:11:24,840 --> 00:11:28,700 Az első jel megragadja a címe a változó n. 125 00:11:28,700 --> 00:11:34,660 Aztán van egy dereference operátor, ami azt jelenti, fogunk ezt a címet a memóriában, 126 00:11:34,660 --> 00:11:36,910 Szóval vissza n. 127 00:11:36,910 --> 00:11:40,910 Most fogd a címét n újra és azonnal dereference, 128 00:11:40,910 --> 00:11:50,780 Szóval vissza n és tárolja 42. 129 00:11:50,780 --> 00:11:55,490 Szóval, egy pár * és csak kioltja. 130 00:11:55,490 --> 00:11:59,980 >> Van egy speciális mutató úgynevezett null mutató. 131 00:11:59,980 --> 00:12:03,140 Ez egy olyan mutató, amit soha ne dereference. 132 00:12:03,140 --> 00:12:07,130 Egy ilyen mutató azért fontos, mert ez ad nekünk egy lehet megkülönböztetni között 133 00:12:07,130 --> 00:12:10,220 a mutató, hogy az kell, és nem kell másolunk. 134 00:12:10,220 --> 00:12:13,050 Ha megpróbálod dereference egy null pointer, 135 00:12:13,050 --> 00:12:17,150 általában a program összeomlik egy szegmentációs hiba, 136 00:12:17,150 --> 00:12:19,210 ami lehet, hogy látott. 137 00:12:19,210 --> 00:12:30,490 Tehát, mondjuk mi a kód int * x = null; * x = 4. 138 00:12:30,490 --> 00:12:36,190 Ebben a példában, úgy tűnhet, nyilvánvaló, hogy csinálunk valami rossz, 139 00:12:36,190 --> 00:12:40,650 de ne feledje, hogy null tényleg egy visszaadott érték hívás funkció 140 00:12:40,650 --> 00:12:45,930 mint malloc, ha malloc nem tudott memóriát a memória a felhasználó által kért. 141 00:12:45,930 --> 00:12:50,200 Emiatt, ha ehelyett az értéket az x egy hívás malloc, 142 00:12:50,200 --> 00:13:12,050 mint az int * x = malloc (sizeof (int)), akkor mindig kifejezetten megtekintéséhez 143 00:13:12,050 --> 00:13:15,280 hogy ha null-ben tért vissza. 144 00:13:15,280 --> 00:13:43,250 Ha az (x == null) / / uhoh! vissza; mást tudunk folytatni, és azt mondják * x = 4. 145 00:13:43,250 --> 00:13:47,780 >> Szóval, megint, miért kellene valaha is használni mutatók? 146 00:13:47,780 --> 00:13:51,910 Nézzünk egy példát a program, ahol meg kell használni mutatók - 147 00:13:51,910 --> 00:13:54,110 egy egyszerű csere funkciót. 148 00:13:54,110 --> 00:14:08,270 Tegyük fel, hogy van két egész, int x = 4, és int y = 15; 149 00:14:08,270 --> 00:14:21,220 és azt akarom, hogy írjon egy függvényt nevű csereügylet, hogy tudom használni valahogy így: swap (x, y). 150 00:14:21,220 --> 00:14:28,270 Ezután ezt a sort, az értékeket belsejét az x változó legyen 15, 151 00:14:28,270 --> 00:14:32,360 és az érték belül változó y kell 4. 152 00:14:32,360 --> 00:14:36,510 Az értékek belső x és y is cserélni. 153 00:14:36,510 --> 00:14:53,040 Nélkül mutatók, talán próbálj ki valami ilyesmit 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 De nem azt veszi észre, a probléma ezzel? 156 00:15:12,960 --> 00:15:19,000 Vegye figyelembe, hogy a tárolt érték a változó egy csak egy másolata az x értéke, 157 00:15:19,000 --> 00:15:22,000 és az értéket ab másolt y. 158 00:15:22,000 --> 00:15:28,000 Minden változtatás történt a és b nem tükröződik x és y. 159 00:15:28,000 --> 00:15:32,050 Így, míg a értékei a és b helyesen cserélve, 160 00:15:32,050 --> 00:15:35,810 X és Y nem változott. 161 00:15:35,810 --> 00:15:38,480 Most változtassuk meg a csere funkciót annak érdekében, hogy érveit 162 00:15:38,480 --> 00:15:42,180 olyan mutatókat változók kell cserélni, valahogy így: 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 Ne feledje, hogy a swap érvek most mutatók, 165 00:16:00,140 --> 00:16:05,670 és ezért meg kell adni a címét, x és y a hívás csere, valahogy így: 166 00:16:05,670 --> 00:16:15,280 csere (& x, & y). 167 00:16:15,280 --> 00:16:20,520 Ez most helyesen swap a az x és y. 168 00:16:20,520 --> 00:16:24,310 >> Nézzünk rajzoljon egy doboz-és nyíl diagram belátni, hogy miért is működik ez. 169 00:16:24,310 --> 00:16:28,520 Kezdjük a két doboz a memóriában, x és y. 170 00:16:28,520 --> 00:16:35,780 Belül a doboz x az a szám, 4, és azon belül a doboz y van 15. 171 00:16:35,780 --> 00:16:41,200 Most, belsejében a hívás a swap függvény, van még két doboz 172 00:16:41,200 --> 00:16:45,140 Az érvek a és b; 173 00:16:45,140 --> 00:16:50,960 Egy pont a doboz x és b rámutat a doboz y. 174 00:16:50,960 --> 00:16:58,070 Egy új doboz jön létre a változó tmp, 175 00:16:58,070 --> 00:17:01,470 és belsejében tároljuk eredménye dereferencing b, 176 00:17:01,470 --> 00:17:04,980 ami azt jelenti, "követi a nyilat a jelölőnégyzetet b." 177 00:17:04,980 --> 00:17:09,880 Szóval, mi tároljuk 15 belül tmp. 178 00:17:09,880 --> 00:17:20,560 Ezután követjük a nyílra b és tárolja itt az eredménye dereferencing a, 179 00:17:20,560 --> 00:17:24,569 amely az érték 4. 180 00:17:24,569 --> 00:17:35,590 Végül kövesse a nyíl, és egy boltban, mi van jelenleg a belsejében tmp, amely 15. 181 00:17:35,590 --> 00:17:42,440 Figyeljük meg, hogy a dobozok jelzett X és Y jelentése helyesen cseréltek értékeket. 182 00:17:42,440 --> 00:17:46,290 >> Amint többet megtudni a malloc és dinamikus memória kezelése, 183 00:17:46,290 --> 00:17:49,610 látni fogjuk, hogy nincs más választásunk, mint hogy mutatók. 184 00:17:49,610 --> 00:17:52,690 Séta a doboz-és nyíl diagram minden program 185 00:17:52,690 --> 00:17:55,980 segít kitalálni, hogy mi a program valóban csinál. 186 00:17:55,980 --> 00:17:59,680 >> A nevem Rob Bowden, és ez CS50. 187 00:18:00,000 --> 00:18:02,500 [CS50.TV] 188 00:18:02,500 --> 00:18:06,070 >> Ez egy másik alkalmazása a csillag - bleah, utálom ezt a szót. 189 00:18:06,070 --> 00:18:13,960 Bárhol szoktuk mondjuk n, most már mondjuk pointer_to_n - nem te Nem látom - * pointer_to_n.