[Powered by Google Translate] [Pointers] [Rob Bowden] [Universiteti i Harvardit] [Kjo është CS50] [CS50.TV] Le të flasim për pointers. Deri tani, ne kemi gjithmonë referuar vetëm për gjërat në kujtesën shprehimisht emër. Ne kemi thënë int n = 42, dhe atëherë kur ne duam të përdorim n ndryshueshme, ne vetëm e quajti atë me emrin e japim atë duke shkruar diçka si n * 2. Por kjo ndryshore duhet të jetojnë diku në kujtesë. Kur ju dëshironi të përdorni vlerën që është ruajtur aktualisht brenda n, ose update vlerën që po mban n, Programi juaj duhet të dini se ku në kujtim të shikoni për n. Ku jeton në kujtesën e ndryshueshme quhet adresa e saj. Është si një adresë shtëpi. Unë mund të gjeni shtëpinë e dikujt për aq kohë sa unë e di adresën e tyre në shtëpi, dhe një program kompjuterik mund të gjeni një ndryshore për aq kohë sa ai e di adresën e saj të kujtesës. Çfarë pointers ofrojnë është një mënyrë të drejtpërdrejt që kanë të bëjnë me këto adresa kujtesës. Një shumë e pushtetit në C vjen nga të qenit në gjendje për të manipuluar kujtesë si kjo. Por me fuqi të madhe vjen përgjegjësi të madhe. Pointers mund të përdoret tejet e mjaftueshme që një shumë e gjuhëve të programimit fshi pointers tërësisht. Pra, pse nuk na japin C pointers pastaj? Siç e dini, argumentet për një funksion C janë kopjuar gjithmonë në parametrat e funksionit. Pra, duke e quajtur diçka si shkëmbim në disa variablave x dhe y nuk mund të zëvendësoj vlerat e x dhe y në funksion quajtur, edhe pse që mund të jetë i dobishëm. Siç do të shohim më vonë, swap rishkrimin për të marrë pointers për vendet që kanë nevojë për të shkëmbehen lejon që ajo të ndikojë variablat thirrësit të saj. Le të ecin nëpër një shembull relativisht të drejtpërdrejtë të asaj pointers mund të bëjë. Le të thonë se ne kemi n int = 4; dhe int * pointer_to_n = & n. Whoa! A bit e sintaksës së re për të mbuluar. Së pari, le të interpretojnë këtë dhe n. Mos harroni se çdo gjë në kujtesë ka disa adresa. The simbol është quajtur "adresa e" operatorit. Kështu, dhe n referohet adresën në memorie ku n është depozituara. Tani, ne jemi ruajtjen këtë adresë në një variabël të re, pointer_to_n. Cili është lloji i këtij variablit të re? Asterisk është një pjesë e tipit variabli-së, dhe ne do të lexoni lloj si * int. * Int thotë se pointer_to_n është një variabël që ruan adresën e një numër të plotë. Ne e dimë se & n është një int * pasi n është një numër të plotë, dhe ne jemi duke marrë adresën e n. * Int është një shembull i një lloji akrep. Sa më shpejt që ju të filloni duke parë asterisks në llojin, ju e dini që ju jeni që kanë të bëjnë me pointers. Ashtu si ne mund të deklarojë një ndryshore si int x dhe y, char ne mund të themi int * Z dhe char * w. Int * dhe * char janë vetëm lloje të reja për ne për t'u përdorur. Vendndodhjen e * mund të shkojnë diku para emrin e ndryshueshme. Pra, të dyja int * pointer_to_n - me * tjetër për int, si ne kemi këtu - dhe int * me * pointer_to_n tjetër për pointer_to_n janë të vlefshme. Por këtu, unë do të vendosin * tjetër për Int. Kjo nuk ka rëndësi që ju preferoni, vetëm të jenë në përputhje. Le të nxjerrë një diagram për këtë. Ne kemi parë n ndryshueshme, të cilat ne do të nxjerrë si një kuti të vogël të kujtesës. Për këtë shembull, le të themi se kjo kuti ndodhet në adresën 100. Brenda këtij kuti, ne jemi ruajtjen e vlerës së 4. Tani, ne kemi një ndryshore të ri, pointer_to_n. Ajo ka kutinë e saj në kujtesë, të cilat ne do të thonë se është në adresën 200. Brenda këtij kuti, ne jemi ruajtjen adresën e n, të cilat ne më parë tha se ishte 100. Shpesh në diagramet, ju do të shihni këtë tregohet si një shigjetë saktë duke lënë kuti pointer_to_n treguar në kutinë që ruan n. Tani, çfarë mund të bëjë në fakt me pointer_to_n? E pra, nëse themi diçka si * pointer_to_n = 8, ky është një përdorim të ndryshëm për asterisk që është plotësisht i ndarë nga përdorimi i yll në shpalljen e një ndryshore e një lloji akrep. Këtu, asterisk quhet operatori dereference. Në diagramin tonë, çfarë * pointer_to_n = 8 do të thotë, shkoni në pointer_to_n kuti përmbajnë, ndiqni arrow, dhe pastaj të caktojë në kutinë në fund të shigjetës vlerën 8. Kjo do të thotë se pas kësaj linje, nëse ne përpiqemi të përdorim n do të ketë vlerën 8. 'Pointer' fjala është përdorur në shumë kontekste të ndryshme. Këtu, ne do të përpiqemi që të jenë në përputhje. Një lloj akrep është diçka si * int. Në këtë video, një akrep do të përdoret vetëm për të do të thotë një vlerë me një lloj akrep, si pointer_to_n cila ka int * Type. Kudo kemi përdorur për të të them vetëm n, ne tani mund të them në vend * pointer_to_n, dhe çdo gjë do të punojë po aq mirë. Le të ecin nëpër një shembull të thjeshtë. Le të thonë se ne kemi n int = 14; int * pointer = &n; n + +, dhe (* treguesin) + +. Linja e parë krijon një kuti të re në kujtesën e emërtuar n. Këtë herë, ne nuk do të emërtim kuti me një adresë të qartë, por ajo ende ka një të tillë. Brenda kuti, ne jemi ruajtjen numrin 14. Linja tjetër krijon një tregues dytë kuti emërtuar. Dhe brenda këtij kuti, ne jemi një tregues për ruajtjen kutinë e etiketuar n. Pra, le të nxjerrë shigjetën nga treguesin në n. Tani, n + + increments vlera në kutinë e etiketuar n, kështu që ne do të shkojmë 14-15. Së fundi, (* akrep) + + shkon në treguesin kuti emërtuar, dereferences vlera në kuti, që do të thotë ndiqni arrow ku ajo tregon, dhe rritjet vlera ruhen atje, kështu që ne do të shkojmë 15-16. Dhe kjo është ajo. N tani ruan numrin 16, pasi që është incremented dy herë - njëherë direkt duke përdorur n ndryshueshme emër, dhe tjetri nëpërmjet një pointer_to_n. Quiz të shpejtë. Çfarë mendoni se kjo do të thotë në qoftë se unë të përpiqet për të thënë diçka si && n? E pra, le të rishkruaj këtë si dhe (& n) i cili realizon të njëjtën gjë. Të (dhe n) kthen adresën e n ndryshueshme në kujtesë. Por pastaj pastaj simbol jashtme përpiqet të kthehen adresën e adresën. Kjo është si duke u përpjekur për të bërë dhe 2. Kjo nuk ka kuptim për të marrë adresën e numrit të vetëm disa pasi ajo nuk është duke u ruajtur në kujtesë. Përdorur dy ampersands në një rresht nuk është ide e drejtë. Por tani, çfarë do të thotë në qoftë se unë të përpiqet për të thënë int ** double_pointer = akrep &? Tani, unë jam duke krijuar një kuti të re etiketuar double_pointer, dhe brenda asaj kutie unë jam ruajtjen adresën e akrep, që do të thotë që unë të nxjerrë një shigjetë nga kutia double_pointer në kutinë akrep. Njoftim llojin e double_pointer, një int **. N ka qenë një, numër të plotë akrep ruhet adresa e n, dhe kështu ajo ka int * Type. Tani, double_pointer ruan adresën e treguesin, kështu që ajo ka tipin int **. Pra, çfarë ne mendojmë se kjo do të thotë - Double_pointer ** = 23? Vini re se unë jam tani dereferencing dy herë. Vetëm ndiqni diagram kuti-dhe-shigjetë ne kemi vendosur tashmë. Së pari, ne do të shkojmë në kutinë e etiketuar double_pointer. E * e parë do të thotë ndiqni arrow herë. Tani, ne jemi në treguesin kuti emërtuar. Ylli i dytë thotë ndiqni arrow përsëri, dhe tani ne jemi në kutinë e etiketuar n, dhe ne kemi ngritur vlerën e këtij kuti të 23. Vini re se "adresa e 'dereference dhe operatorët janë inverses të njëri-tjetrit. Kjo lejon mua për të bërë diçka si * & * & n = 42. Ndërsa kjo punon, ju nuk duhet të bëni diçka si kjo në praktikë. Çfarë po ne fakt bën këtu? The simbol parë grabs adresën e n ndryshueshme. Pastaj, ne kemi një operator dereference, që do të thotë ne jemi duke shkuar në atë adresë në kujtesën, kështu që ne jemi kthyer në n. Tani, ne kap adresën e n përsëri dhe menjëherë dereference, kështu që ne jemi kthyer në n dhe dyqan 42. Pra, çdo palë e * dhe vetëm anulon jashtë. Nuk është një tregues të veçantë të quajtur pointer null. Ky është një tregues që ne kurrë nuk duhet të dereference. Një tregues i tillë është i rëndësishëm, sepse ajo na jep një mënyrë për të dalluar mes një tregues që duhet dhe nuk duhet të dereferenced. Nëse ju përpiqeni të dereference një tregues null, zakonisht programi juaj do të rrëzimit me një defekti segmentimit, të cilat ju mund të keni parë më parë. Pra, le të thonë se ne kemi kodin int * x = null; * x = 4. Në këtë shembull, kjo mund të duket e qartë se ne jemi duke bërë diçka të keqe, por mos harroni se null vërtetë mund të jetë një vlerë e kthyer nga një thirrje për një funksion si malloc, nëse malloc është në gjendje të ndajë kujtesën e kërkuar nga përdoruesit. Për këtë arsye, në qoftë se në vend të kësaj ne kemi vendosur vlerën e x nga një thirrje për malloc, si në int * x = malloc (sizeof (int)), atëherë ne duhet të kontrolloni në mënyrë eksplicite për të parë nëse null u kthye. Në qoftë se (x == null); / / uhoh! kthimit; tjetër ne mund të vazhdojë më dhe them * x = 4. Pra, përsëri, pse ne duhet të përdorin kurrë pointers? Le të shikojmë në një shembull të një programi, ku ne kemi nevojë për të përdorur pointers - një funksion të thjeshtë swap. Le të thonë se unë kam dy integers, int x = 4; dhe int y = 15; dhe unë dua të shkruaj një funksion të quajtur swap që unë mund të përdorni si kështu: swap (x, y). Pas kësaj linje, vlerat brenda e ndryshueshme x duhet të jetë 15, dhe vlera brenda y variabël duhet të jetë 4. Vlerat brenda të x dhe y janë swapped. Pa pointers, ne mund të provoni diçka si shkëmbim void (int a, int b); int tmp = b, b = a, një = tmp. Por, a vëreni problem me këtë? Mos harroni se vlera e ruajtur në një ndryshore është vetëm një kopje e vlerës së x, dhe vlera në B është kopjuar nga y. Çdo ndryshimet e bëra në A dhe B nuk do të reflektohet në x dhe y. Kështu, ndërsa vlerat e A dhe B janë swapped saktë, x dhe y nuk kanë ndryshuar fare. Tani, le të ndryshojë funksionin swap në mënyrë që argumentet e saj janë pointers të variablave që duhet të bie në ujdi, si kështu: swap void (int * a, int * b); int tmp = * b, * b = * a, * a = tmp. Mos harroni se këmbime argumentet janë tani pointers, dhe kështu që ne duhet të kalojnë adresën e x dhe y në thirrjen për shkëmbim, si kështu: swap (& x, & y). Kjo tani saktë këmbime vlerat e x dhe y. Le të nxjerrë një diagram kuti-dhe-shigjetë për të parë se pse kjo punon. Ne fillim me kuti dy në kujtim, x dhe y. Brenda e kutisë për x është numri 4, dhe brenda e kutisë per y ne kanë 15. Tani, brenda thirrjes në funksion swap, ne kemi dy kuti më shumë për argumenteve a dhe b; një pikë në kutinë për X, dhe pikat b në kutinë për y. Një kuti e re është krijuar për tmp ndryshueshme, dhe brenda saj kemi ruajtur rezultatin e dereferencing b, cila do të thotë 'ndjekur shigjeten nga kutia etiketuar b.' Pra, ne kemi ruajtur brenda 15 tmp. Pastaj, ne ndiqni arrow në b dhe dyqan këtu rezultatin e dereferencing një, cili është 4 vlera. Së fundi, ne kemi ndiqni arrow në një dyqan dhe çfarë është aktualisht brenda tmp, e cila është 15. Vini re se kutitë e emërtuar x dhe y janë swapped saktë vlera. Sapo kemi mësuar më shumë rreth malloc dhe menaxhimit dinamik kujtesës, ne do të shohim se kemi asnjë zgjidhje, por për të përdorur pointers. Ecje përmes diagram kuti-dhe-shigjetë për çdo program mund të ju ndihmojë të kuptoj se çfarë programi është me të vërtetë duke bërë. Emri im është Rob Bowden, dhe kjo është CS50. [CS50.TV] Kjo është një përdorim të ndryshëm për yll - bleah, unë e urrej atë fjalë. Kudo kemi përdorur për të të them vetëm n, ne tani mund të themi pointer_to_n - nuk ju can't - * pointer_to_n.