1 00:00:00,000 --> 00:00:02,440 [Powered by Google Translate] [Ukazovatele] 2 00:00:02,440 --> 00:00:05,000 [Rob Bowden] [Harvard University] 3 00:00:05,000 --> 00:00:07,360 [To je CS50] [CS50.TV] 4 00:00:07,360 --> 00:00:08,820 >> Poďme hovoriť o ukazovatele. 5 00:00:08,820 --> 00:00:13,710 Až do teraz, sme vždy len odvolával sa na veci v pamäti explicitne podľa názvu. 6 00:00:13,710 --> 00:00:22,610 Povedali sme int n = 42, a potom, keď chceme použiť premennú n, 7 00:00:22,610 --> 00:00:30,640 sme len zavolať ju menom dáme ju písať niečo ako n * 2. 8 00:00:30,640 --> 00:00:34,790 Ale že premenná musí žiť niekde v pamäti. 9 00:00:34,790 --> 00:00:37,790 Ak chcete použiť hodnotu, ktorá je v súčasnosti uložená v n, 10 00:00:37,790 --> 00:00:40,730 alebo aktualizovať hodnotu, ktorá n je drží, 11 00:00:40,730 --> 00:00:44,180 váš program potrebuje vedieť, kde v pamäti hľadať pre n 12 00:00:44,180 --> 00:00:48,320 Ak v pamäti je premenná žije volal jeho adresu. 13 00:00:48,320 --> 00:00:49,940 Je to ako dom adresu. 14 00:00:49,940 --> 00:00:53,080 Nemôžem nájsť niečí dom tak dlho, ako viem, svoju adresu, 15 00:00:53,080 --> 00:00:58,110 a počítačový program môžete nájsť premennú tak dlho, ako to pozná jeho adresu v pamäti. 16 00:00:58,110 --> 00:01:02,660 Čo ukazovatele poskytujú, je spôsob, ako priamo sa zaoberať týmito pamäťovými adresami. 17 00:01:02,660 --> 00:01:06,860 >> Veľa energie v C pochádza z byť schopný manipulovať pamäte, ako je tento. 18 00:01:06,860 --> 00:01:09,960 Ale s veľkou mocou prichádza aj veľká zodpovednosť. 19 00:01:09,960 --> 00:01:14,670 Ukazovatele môžu byť použité nebezpečne natoľko, že mnoho programovacích jazykov 20 00:01:14,670 --> 00:01:16,460 skryť ukazovatele úplne. 21 00:01:16,460 --> 00:01:19,440 Tak, prečo C nám odkazy potom? 22 00:01:19,440 --> 00:01:22,680 Ako viete, argumenty funkcie C 23 00:01:22,680 --> 00:01:25,370 sú vždy skopírovaná do parametrov funkcie. 24 00:01:25,370 --> 00:01:33,260 Takže, volať niečo ako odkladací priestor na niektorých premenných x a y 25 00:01:33,260 --> 00:01:38,840 nemôže striedať hodnoty X a Y v volajúci funkciu, 26 00:01:38,840 --> 00:01:40,810 aj keď to môže byť užitočné. 27 00:01:40,810 --> 00:01:46,430 Ako uvidíme neskôr, prepisovanie odkladacia aby ukazovatele na umiestnenie, ktoré je potrebné vymeniť 28 00:01:46,430 --> 00:01:49,690 umožňuje ovplyvniť jeho návštevníkovi je premenné. 29 00:01:49,690 --> 00:01:54,150 >> Poďme prejsť pomerne priamočiare príklad toho, čo môže urobiť ukazovatele. 30 00:01:54,150 --> 00:02:15,550 Povedzme, že máme int n = 4, a int * pointer_to_n = & n 31 00:02:15,550 --> 00:02:18,990 Whoa! Trochu novú syntax na krytie. 32 00:02:18,990 --> 00:02:22,600 Po prvé, poďme to vyložiť & n 33 00:02:22,600 --> 00:02:27,260 Pamätajte si, že všetko, čo v pamäti má nejakú adresu. 34 00:02:27,260 --> 00:02:30,800 Ampersand sa nazýva "adresa" subjektu. 35 00:02:30,800 --> 00:02:36,470 Takže, & n odkazuje na adresu v pamäti, kde je uložený n 36 00:02:36,470 --> 00:02:41,560 Teraz sa ukladajú túto adresu do novej premennej, pointer_to_n. 37 00:02:41,560 --> 00:02:43,870 Aký je typ tejto novej premennej? 38 00:02:43,870 --> 00:02:47,410 Hviezdička je súčasťou typu premennej, 39 00:02:47,410 --> 00:02:49,880 a budeme čítať ako typ int *. 40 00:02:49,880 --> 00:02:56,500 Int * znamená, že pointer_to_n je premenná, ktorá ukladá adresu integer. 41 00:02:56,500 --> 00:03:02,970 Vieme, že a n je int * od n je celé číslo, a my sme sa ujali adresu n. 42 00:03:02,970 --> 00:03:06,660 Int * je príklad typ ukazovateľa. 43 00:03:06,660 --> 00:03:10,150 Akonáhle začnete vidieť Hviezdičky v typu, 44 00:03:10,150 --> 00:03:11,950 viete, že máte čo do činenia s ukazovateľmi. 45 00:03:11,950 --> 00:03:16,520 Rovnako ako môžeme deklarovať premennú ako int x a y, char 46 00:03:16,520 --> 00:03:20,410 môžeme povedať, int * Z a char * w. 47 00:03:20,410 --> 00:03:25,190 Int * a char * sú len nové typy pre nás používať. 48 00:03:25,190 --> 00:03:29,430 Umiestnenie * môže ísť kamkoľvek pred názvom premennej. 49 00:03:29,430 --> 00:03:34,730 Takže, ako int * pointer_to_n - s * vedľa int, ako sme tu - 50 00:03:34,730 --> 00:03:45,210 a int * pointer_to_n s * vedľa pointer_to_n sú platné. 51 00:03:45,210 --> 00:03:56,470 Ale tu, budem umiestnite * vedľa int. 52 00:03:56,470 --> 00:04:00,600 Nezáleží na tom, ktoré dávate prednosť, jednoducho byť v súlade. 53 00:04:00,600 --> 00:04:02,810 >> Poďme nakresliť schému pre tento. 54 00:04:02,810 --> 00:04:07,590 Musíme najprv premennú n, ktoré budeme čerpať ako krabičke pamäte. 55 00:04:07,590 --> 00:04:15,400 Pre tento príklad, povedzme, že toto políčko je umiestnená na adrese 100. 56 00:04:15,400 --> 00:04:18,820 Vnútri tohto poľa, sme uloženie hodnotu 4. 57 00:04:18,820 --> 00:04:23,730 Teraz máme novú premennú, pointer_to_n. 58 00:04:23,730 --> 00:04:27,030 Má svoj vlastný box v pamäti, 59 00:04:27,030 --> 00:04:32,900 ktoré budeme hovoriť, je na adrese 200. 60 00:04:32,900 --> 00:04:37,220 Vnútri tohto poľa, sme ukladanie adresu n, 61 00:04:37,220 --> 00:04:39,890 ktoré sme predtým hovorili bolo 100. 62 00:04:39,890 --> 00:04:44,710 Často na obrázkoch, uvidíte to ukáže ako doslovný šípky 63 00:04:44,710 --> 00:04:48,730 opúšťať pointer_to_n box ukazuje na pole, ktoré ukladá n 64 00:04:48,730 --> 00:04:54,620 Teraz, čo môžeme skutočne urobiť s pointer_to_n? 65 00:04:54,620 --> 00:05:10,400 No, keď povieme niečo ako * pointer_to_n = 8, to je iná použitie pre hviezdička 66 00:05:10,400 --> 00:05:14,830 že je úplne oddelená od použitia hviezdičkou v deklarovaní premennej 67 00:05:14,830 --> 00:05:16,790 o ukazovatele typu. 68 00:05:16,790 --> 00:05:21,130 Tu, hviezdička je nazýva dereferencia operátor. 69 00:05:21,130 --> 00:05:26,860 V našom diagrame, čo * pointer_to_n = 8 znamená, 70 00:05:26,860 --> 00:05:32,220 prejsť na pole obsahujúce pointer_to_n, za šípkou, 71 00:05:32,220 --> 00:05:38,160 a potom priradiť pole na konci šípky hodnota 8. 72 00:05:38,160 --> 00:05:45,960 To znamená, že po tejto línie, ak sa pokúsi použiť n, že bude mať hodnotu 8. 73 00:05:45,960 --> 00:05:51,600 Slovo "ukazovateľ" sa používa v mnohých rôznych kontextoch. 74 00:05:51,600 --> 00:05:54,380 Tu sa budeme snažiť byť v súlade. 75 00:05:54,380 --> 00:05:58,330 Ukazovateľ typu je niečo ako int *. 76 00:05:58,330 --> 00:06:04,630 V tomto videu, bude ukazovateľ byť použité iba pre vyjadrenie hodnoty s typ ukazovateľa, 77 00:06:04,630 --> 00:06:08,180 ako pointer_to_n ktorá má typ int *. 78 00:06:08,180 --> 00:06:15,140 Kdekoľvek sme len povedať n, môžeme sa teraz namiesto povedať * pointer_to_n, 79 00:06:15,140 --> 00:06:18,020 a všetko bude fungovať rovnako dobre. 80 00:06:18,020 --> 00:06:21,120 >> Prejdime ďalšie jednoduchý príklad. 81 00:06:21,120 --> 00:06:50,390 Povedzme, že máme int n = 14; int * ukazovateľ = &n; n + +, a (* ukazovateľ) + +. 82 00:06:50,390 --> 00:06:59,830 Prvý riadok vytvorí nový rámik v pamäti označené n 83 00:06:59,830 --> 00:07:05,400 Tentoraz nebudeme označiť políčko s výslovným adresou, ale stále má jeden. 84 00:07:05,400 --> 00:07:11,810 Vnútri krabice, sme uloženie čísla 14. 85 00:07:11,810 --> 00:07:22,290 Na ďalší riadok vytvorí druhý políčko ukazovateľ. 86 00:07:22,290 --> 00:07:27,210 A vo vnútri tohto poľa, sme uloženie ukazovateľ na pole s označením n 87 00:07:27,210 --> 00:07:33,170 Takže, poďme nakreslite šípku z ukazovateľa na n 88 00:07:33,170 --> 00:07:37,790 Teraz, n + + inkrementuje hodnotu v poli označenom n, 89 00:07:37,790 --> 00:07:45,420 tak ideme 14-15. 90 00:07:45,420 --> 00:07:53,330 Konečne, (* ukazovateľ) + + ide do krabice označenej ukazovatele, 91 00:07:53,330 --> 00:08:02,660 dereferences hodnota v poli, čo znamená, že za šípkou na miesto, kde sa uvádza, 92 00:08:02,660 --> 00:08:11,690 a zvyšuje sa hodnota uloží tam, tak ideme 15-16. 93 00:08:11,690 --> 00:08:13,480 A to je všetko. 94 00:08:13,480 --> 00:08:18,480 N teraz ukladá počet 16 potom, čo bol zvýšený dvakrát - 95 00:08:18,480 --> 00:08:25,050 raz priamo pomocou názvu premennej n, a druhá cez pointer_to_n. 96 00:08:25,050 --> 00:08:33,360 >> Rýchly kvíz. Čo si myslíte, že to znamená, keď sa snažím povedať niečo ako && n? 97 00:08:33,360 --> 00:08:41,350 Dobre, poďme prepísať toto ako a (a n), ktorý vykoná rovnakú vec. 98 00:08:41,350 --> 00:08:47,030 The (a n) vracia adresu premennej N v pamäti. 99 00:08:47,030 --> 00:08:53,110 Ale potom kliknite na vonkajšie ampersand snaží vrátiť adresu adresy. 100 00:08:53,110 --> 00:08:56,600 To je ako snažiť sa robiť a 2. 101 00:08:56,600 --> 00:09:00,550 To nedáva zmysel získať adresu iba nejakým číslom 102 00:09:00,550 --> 00:09:03,260 pretože to nie sú uložené v pamäti. 103 00:09:03,260 --> 00:09:07,090 Pomocou dvoch Ampersand v rade nie je nikdy správna myšlienka. 104 00:09:07,090 --> 00:09:28,960 Ale teraz, čo to znamená, keď sa snažím povedať, int ** double_pointer = & ukazovateľ? 105 00:09:28,960 --> 00:09:40,750 Teraz som vytvoriť novú políčko double_pointer, 106 00:09:40,750 --> 00:09:44,590 a vo vnútri tej krabice som ukladanie adresu ukazovatele, 107 00:09:44,590 --> 00:09:50,810 čo znamená, že nakresliť šípku z krabice double_pointer na ukazovateľ poľa. 108 00:09:50,810 --> 00:09:56,640 Všimnite si, že typ double_pointer, int **. 109 00:09:56,640 --> 00:10:03,700 N bol integer, ukazovateľ uložený adresu n, a tak to má typ int *. 110 00:10:03,700 --> 00:10:10,550 Teraz, double_pointer ukladá adresu ukazovateľ, tak to má typ int **. 111 00:10:10,550 --> 00:10:15,070 >> Takže, čo si myslíme, že to znamená - 112 00:10:15,070 --> 00:10:24,490 ** Double_pointer = 23? 113 00:10:24,490 --> 00:10:28,630 Všimnite si, že som teraz dereferencing dvakrát. 114 00:10:28,630 --> 00:10:32,030 Stačí sledovať box a šípkou diagram sme už nastavili. 115 00:10:32,030 --> 00:10:36,400 Po prvé, sme sa ísť do poľa označeného double_pointer. 116 00:10:36,400 --> 00:10:40,550 Prvý * znamená nasledovať raz šípku. 117 00:10:40,550 --> 00:10:44,110 Teraz sme v pokladni označené ukazovateľom. 118 00:10:44,110 --> 00:10:49,940 Druhá hviezda hovorí, že nasledovať šípku znovu, 119 00:10:49,940 --> 00:10:58,230 a teraz sme na poli označenom n, a nastavíme hodnotu tohto poľa 23. 120 00:10:58,230 --> 00:11:05,940 Všimnite si, že dereferencia a "adresa" operátorov sú inverses navzájom. 121 00:11:05,940 --> 00:11:16,990 To mi umožňuje urobiť niečo ako * & * & n = 42. 122 00:11:16,990 --> 00:11:22,550 Kým to funguje, mali by ste sa nikdy niečo také v praxi. 123 00:11:22,550 --> 00:11:24,840 Čo sme vlastne tu? 124 00:11:24,840 --> 00:11:28,700 Prvý ampersand chytí adresu premennej n 125 00:11:28,700 --> 00:11:34,660 Potom máme dereferencia operátor, čo znamená, že sa budeme na túto adresu v pamäti, 126 00:11:34,660 --> 00:11:36,910 takže sme zase na n 127 00:11:36,910 --> 00:11:40,910 Teraz uchopiť adresu n znovu a okamžite dereferencia, 128 00:11:40,910 --> 00:11:50,780 Takže sme späť u n a sklad 42. 129 00:11:50,780 --> 00:11:55,490 Takže, každý pár * a len ruší. 130 00:11:55,490 --> 00:11:59,980 >> K dispozícii je špeciálny ukazovateľ nazvaný nulový ukazovateľ. 131 00:11:59,980 --> 00:12:03,140 To je ukazovateľ, ktorý by sme mali nikdy dereferencia. 132 00:12:03,140 --> 00:12:07,130 Takýto ukazovateľ je dôležitý, pretože nám dáva spôsob, ako rozlišovať medzi 133 00:12:07,130 --> 00:12:10,220 ukazovateľ, ktorý by mal a nemal byť dereferenced. 134 00:12:10,220 --> 00:12:13,050 Ak sa pokúsite dereferencia ukazovatele null, 135 00:12:13,050 --> 00:12:17,150 obvykle váš program spadne s Segmentation fault, 136 00:12:17,150 --> 00:12:19,210 ktoré ste možno nevideli. 137 00:12:19,210 --> 00:12:30,490 Takže, povedzme, že máme kód int * x = null; * x = 4. 138 00:12:30,490 --> 00:12:36,190 V tomto príklade, sa môže zdať zrejmé, že robíme niečo zlé, 139 00:12:36,190 --> 00:12:40,650 ale nezabudnite, že null môže byť skutočne hodnota vrátená z volania funkcie 140 00:12:40,650 --> 00:12:45,930 ako malloc, ak malloc nie je schopný prideliť pamäť požadované užívateľom. 141 00:12:45,930 --> 00:12:50,200 Z tohto dôvodu sa v prípade namiesto toho nastaviť hodnotu x z volania malloc, 142 00:12:50,200 --> 00:13:12,050 ako v int * x = malloc (sizeof (int)), potom by sme mali vždy explicitne kontrolovať 143 00:13:12,050 --> 00:13:15,280 či null bol vrátený. 144 00:13:15,280 --> 00:13:43,250 If (x == null); / / UHOH! return; ešte môžeme pokračovať ďalej a hovoriť * x = 4. 145 00:13:43,250 --> 00:13:47,780 >> Takže znova, prečo by sme mali vždy používať ukazovatele? 146 00:13:47,780 --> 00:13:51,910 Poďme sa pozrieť na príklad programu, kde musíme používať ukazovatele - 147 00:13:51,910 --> 00:13:54,110 jednoduchá výmena funkcií. 148 00:13:54,110 --> 00:14:08,270 Povedzme, že mám dve celé čísla, int x = 4, a int y = 15; 149 00:14:08,270 --> 00:14:21,220 a chcem napísať funkciu nazvanú swap, ktorý môžem použiť ako tak: odkladacie (x, y). 150 00:14:21,220 --> 00:14:28,270 Po tejto línie, mali by sa hodnoty v rámci premennej x je 15, 151 00:14:28,270 --> 00:14:32,360 a hodnota v premennej y mal byť 4. 152 00:14:32,360 --> 00:14:36,510 Hodnoty vnútorná časť x a y boli vymenené. 153 00:14:36,510 --> 00:14:53,040 Bez ukazovatele, môžeme skúsiť niečo ako void swapu (int, int b); 154 00:14:53,040 --> 00:15:09,750 int tmp = b, b =; = tmp. 155 00:15:09,750 --> 00:15:12,960 Ale, to si všimnete problém s tým? 156 00:15:12,960 --> 00:15:19,000 Nezabudnite, že hodnota uložená v premennej je len kópia hodnoty x, 157 00:15:19,000 --> 00:15:22,000 a hodnota vb je skopírovaný z y. 158 00:15:22,000 --> 00:15:28,000 Všetky zmeny vykonané v a a b sa neprejaví v x a y. 159 00:15:28,000 --> 00:15:32,050 Takže, zatiaľ čo hodnoty a a b sú správne prehodenie, 160 00:15:32,050 --> 00:15:35,810 x a y sa vôbec nezmenila. 161 00:15:35,810 --> 00:15:38,480 Teraz, poďme zmeniť odkladacie funkciu tak, že jej argumenty 162 00:15:38,480 --> 00:15:42,180 sú ukazovatele na premenné treba swapového, ako tak: 163 00:15:42,180 --> 00:15:56,880 void swap (int *, int * b); int tmp = * b; * b = *, * = tmp. 164 00:15:56,880 --> 00:16:00,140 Pamätajte si, že swapy argumenty sú teraz ukazovatele, 165 00:16:00,140 --> 00:16:05,670 a tak musíme odovzdať adresu x a y vo výzve k výmene, ako tak: 166 00:16:05,670 --> 00:16:15,280 swap (a x, a y). 167 00:16:15,280 --> 00:16:20,520 Toto teraz správne swapy sú hodnoty x a y. 168 00:16:20,520 --> 00:16:24,310 >> Poďme nakresliť box-and-šípky diagramu vidieť prečo to funguje. 169 00:16:24,310 --> 00:16:28,520 Začneme s našimi dvoma boxy v pamäti, X a Y. 170 00:16:28,520 --> 00:16:35,780 Vnútri boxu pre x je číslo 4, a vo vnútri boxu pre y máme 15. 171 00:16:35,780 --> 00:16:41,200 Teraz, vo vnútri volania do swapu funkcie, máme dve ďalšie krabice 172 00:16:41,200 --> 00:16:45,140 pre argumenty a a b; 173 00:16:45,140 --> 00:16:50,960 a poukazuje na pole pre x, a, b ukazuje na pole pre y. 174 00:16:50,960 --> 00:16:58,070 Nový box je vytvorený pre premenné tmp, 175 00:16:58,070 --> 00:17:01,470 a vo vnútri nej uložíme výsledok dereferencing b, 176 00:17:01,470 --> 00:17:04,980 čo znamená, že "za šípkou z krabice označenej b" 177 00:17:04,980 --> 00:17:09,880 Takže, uložíme 15 vnútri tmp. 178 00:17:09,880 --> 00:17:20,560 Potom sledujeme na šípku b a uložiť tu výsledok dereferencing, 179 00:17:20,560 --> 00:17:24,569 čo je hodnota 4. 180 00:17:24,569 --> 00:17:35,590 Konečne, sledujeme na šípku v obchode a čo je v súčasnej dobe vo vnútri tmp, ktoré je 15 rokov. 181 00:17:35,590 --> 00:17:42,440 Všimnite si, že tieto boxy označené x a y správne vymenili hodnoty. 182 00:17:42,440 --> 00:17:46,290 >> Akonáhle sa dozvieme viac o malloc a dynamickú správu pamäte, 183 00:17:46,290 --> 00:17:49,610 uvidíme, že nemáme inú možnosť, než použiť ukazovatele. 184 00:17:49,610 --> 00:17:52,690 Prechádzky box-a-šípka diagramu pre každý program 185 00:17:52,690 --> 00:17:55,980 vám môže pomôcť zistiť, čo program skutočne robí. 186 00:17:55,980 --> 00:17:59,680 >> Moje meno je Rob Bowden, a to je CS50. 187 00:18:00,000 --> 00:18:02,500 [CS50.TV] 188 00:18:02,500 --> 00:18:06,070 >> To je iná použitie pre hviezdičku - bleah, neznášam to slovo. 189 00:18:06,070 --> 00:18:13,960 Kdekoľvek sme len povedať n, môžeme teraz povedať, pointer_to_n - no ty nemôžeš - * pointer_to_n.