[Powered by Google Translate] [Pointers] [Rob Bowden] [Harvard University] [Dit is CS50] [CS50.TV] Laten we praten over pointers. Tot nu toe hebben we altijd zojuist genoemde zaken in het geheugen expliciet bij naam. We hebben gezegd int n = 42, en toen we de variabele n te gebruiken, We noemen het gewoon door de naam die we geven door het schrijven van iets als n * 2. Maar die variabele moet ergens in het geheugen te leven. Wanneer u de waarde die op dat moment wordt opgeslagen in n te gebruiken, of om de waarde die n houdt, het programma moet weten waar in het geheugen te zoeken n. Waar in het geheugen van een variabele leven heet zijn adres. Het is net als een huis adres. Ik kan iemand het huis zo lang als ik weet dat hun huisadres, en een computer programma kan een variabele zolang het geheugenadres kent. Wat pointers bieden is een manier om direct het omgaan met deze geheugenadressen. Een groot deel van de macht in C komt van de mogelijkheid om het geheugen als deze te manipuleren. Maar met grote macht komt grote verantwoordelijkheid. Pointers kan gevaarlijk genoeg worden gebruikt die veel programmeertalen volledig verstoppen pointers. Dus, waarom C geeft ons aanwijzingen dan? Zoals u weet, argumenten voor een C-functie altijd gekopieerd naar de parameters van de functie. Dus, bellen iets als swap op sommige variabelen x en y kan de waarden van x en y verwisselen in de aanroepfunctie, ook al is dat misschien wel handig. Zoals we later zullen zien, het herschrijven van swap om pointers te nemen om de locaties om te wisselen maakt het mogelijk om invloed op de beller variabelen. Laten we lopen door een relatief eenvoudige voorbeeld van wat pointers kan doen. Laten we zeggen dat we int n = 4, en int * pointer_to_n = & n. Whoa! Een beetje nieuwe syntax te dekken. Laten we eerst eens interpreteren & n. Vergeet niet dat alles wat in het geheugen van sommige adres heeft. De ampersand wordt genoemd de "adres van" operator. Dus & n verwijst naar het adres in het geheugen waar n is opgeslagen. Nu zijn we het opslaan van dit adres in een nieuwe variabele, pointer_to_n. Wat is het type van deze nieuwe variabele? De asterisk is een deel van het variabeletype, en wij zullen lezen het type als int *. Int * betekent dat pointer_to_n is een variabele die het adres van een integer opgeslagen. We weten dat & n een int *, omdat n een geheel getal, en we nemen het adres van n. Int * is een voorbeeld van een pointer type. Zodra u begint met het zien van sterretjes in het type, je weet dat je te maken hebt met pointers. Net zoals we kunnen verklaren een variabele als int x en y char, kunnen we zeggen int * z en char * w. Int * en char * zijn gewoon nieuwe soorten voor ons gebruiken. De locatie van het * kan overal gaan voor de naam van de variabele. Dus, zowel int * pointer_to_n - met de * naast int, want we hebben hier - en int * pointer_to_n met de * naast pointer_to_n geldig zijn. Maar hier, zal ik de * naast int. Het maakt niet uit waar u wilt, gewoon consequent zijn. Laten we trekken een schema voor. We moeten eerst de variabele n, die we trekken een doosje van het geheugen. Voor dit voorbeeld, laten we zeggen dat deze box bevindt zich op het adres 100. Binnenkant van deze box, we slaan de waarde 4. Nu hebben we een nieuwe variabele, pointer_to_n. Het heeft een eigen vak in het geheugen die we zullen zeggen, is op het adres 200. Binnenkant van deze box zijn we het opslaan van het adres van n, die voordat we zeiden was 100. Vaak in diagrammen, zie je dit weergegeven als een letterlijke pijl het verlaten van de pointer_to_n vakje te wijzen op het vak dat slaat n. Nu, wat kunnen we eigenlijk doen met pointer_to_n? Nou, als we iets zeggen als * pointer_to_n = 8, dit is een ander gebruik voor het sterretje die volledig gescheiden is van het gebruik van het sterretje in waarbij een variabele een pointer type. Hier wordt de asterisk genoemd dereference operator. In onze diagram, wat * pointer_to_n = 8 betekent, ga naar de doos met pointer_to_n, volg de pijl, en dan toewijzen aan de kader aan het eind van de pijl de waarde 8. Dit betekent dat na deze lijn, wanneer we proberen te gebruiken n zal de waarde 8. Het woord 'pointer' wordt gebruikt in veel verschillende contexten. Hier zullen we proberen om consistent te zijn. Een pointer type is zoiets als int *. In deze video wordt een pointer alleen gebruikt om een ​​waarde met een aanwijzertype betekenen zoals pointer_to_n die moet het type int *. Overal hebben we gebruikt om alleen maar zeggen n, kunnen we nu in plaats zeggen * pointer_to_n, en alles zal net zo goed werken. Laten we lopen door een ander eenvoudig voorbeeld. Laten we zeggen dat we int n = 14; int * pointer = &N; n + +, en (* pointer) + +. De eerste regel maakt een nieuw vak in het geheugen label n. Deze keer zullen we niet het etiket van de doos met een expliciete adres, maar het heeft nog steeds een. Binnenkant van de doos, we slaan het nummer 14. De volgende regel creëert een tweede vakje pointer. En de binnenkant van deze box, we slaan een verwijzing naar het vak n. Dus, laten we trekken de pijl van pointer naar n. Nu n + + verhoogt de waarde in het vak n, dus we gaan 14 tot 15. Tot slot, (* pointer) + + gaat naar het vak aanwijzer, referentie aan de waarde in het vak, wat betekent volg de pijl naar de plaats waar het verwijst, en verhoogt de waarde daar opgeslagen, dus gaan we 15 tot 16. En dat is het. N slaat nu de nummer 16 na twee keer te zijn verhoogd - eenmaal rechtstreeks door de variabele n, en de andere over een pointer_to_n. Snelle quiz. Wat denk je dat het betekent als ik probeer om iets als && zeggen n? Nou, laten we herschrijven het als & (& n) die hetzelfde volbrengt. De (& n) geeft het adres van de variabele n in het geheugen. Maar dan vervolgens buitenste teken probeert om terug te keren het adres van het adres. Dat is hetzelfde als proberen te doen en 2. Het heeft geen zin om het adres van slechts een getal te krijgen want het is niet in het geheugen opgeslagen. Met behulp van twee ampersands op rij is nooit het juiste idee. Maar nu, wat betekent het als ik probeer int ** double_pointer = & pointer zeggen? Nu, ik ben het creëren van een nieuw vakje double_pointer, en de binnenkant van die doos ik het opslaan van het adres van pointer, wat betekent dat ik teken een pijl uit de double_pointer doos om de aanwijzer doos. Let op de aard van de double_pointer, een int **. N is een geheel getal, pointer opgeslagen adres n, en heeft dus int *. Nu, double_pointer slaat het adres van pointer, dus het heeft het type int **. Dus, wat we denken dat dit betekent - ** Double_pointer = 23? Merk op dat ik nu dereferentie tweemaal. Volg gewoon de box-and-pijldiagram we al hebt ingesteld. Ten eerste gaan we naar het vak double_pointer. De eerste * betekent een keer volg de pijl. Nu, we zijn op het vak aanwijzer. De tweede ster zegt weer volg de pijl, en nu zijn we op het vakje n, en zetten we de waarde van dit vak tot en met 23. Merk op dat de dereference en 'adres van' operators zijn inverse van elkaar. Dit stelt me ​​in staat om iets te doen zoals * & * & n = 42. Hoewel dit werkt, moet je nooit iets doen als dit in de praktijk. Wat zijn we eigenlijk aan het doen hier? De eerste ampersand grijpt het adres van de variabele n. Dan hebben we een dereference operator, wat betekent dat we gaan naar dat adres in het geheugen, dus we zijn weer terug bij n. Nu, pakken we het adres van n opnieuw en onmiddellijk dereference, dus we zijn weer terug bij n en op te slaan 42. Dus, elk paar * & net annuleert. Er is een speciale pointer genaamd de null pointer. Dit is een pointer die we moeten nooit dereference. Dergelijke pointer is belangrijk omdat het ons een manier om onderscheid een pointer die wel en niet moet worden dereferentie. Als u probeert om dereferentie een null pointer, meestal uw programma zal crashen met een segmentation fault, die u misschien eerder hebt gezien. Dus, laten we zeggen dat we de code int * x = null; * x = 4. In dit voorbeeld kan het lijken voor de hand dat we iets slechts doen, maar vergeet niet dat null really kan een waarde terug van een oproep naar een functie zoals malloc als malloc staat is het geheugen verzoek van de gebruiker te wijzen. Om deze reden, als we in plaats daarvan de waarde van x van een oproep tot malloc, zoals in int * x = malloc (sizeof (int)), dan moeten we altijd expliciet te controleren om te zien of null geretourneerd. If (x == null) / / uhoh! terugkeer; anders kunnen we verder en zeggen * x = 4. Dus nogmaals, waarom zouden we ooit gebruiken pointers? Laten we eens kijken naar een voorbeeld van een programma waar we moeten pointers - een eenvoudige swap functie. Laten we zeggen dat ik twee gehele getallen, int x = 4, en int y = 15; en ik wil een functie genaamd swap die ik kan gebruiken als zo schrijven: swap (x, y). Na deze regel moet de waarden binnen de variabele x 15, en de waarde in variabele y moet 4. De waarden binnen x en y zijn verwisseld. Zonder pointers, kunnen we iets als leegte swap proberen (int a, int b); int tmp = b, b = a; a = tmp. Maar, merkt u het probleem met dit? Herinner dat de waarde opgeslagen in de variabele a slechts een kopie van de waarde van x, en de waarde b gekopieerd van y. Wijzigingen in a en b wordt niet weerspiegeld in x en y. Dus, terwijl de waarden van a en b kunnen verwisseld x en y zijn niet veranderd. Nu, veranderen we de swap-functie, zodat de argumenten zijn verwijzingen naar de variabelen moet verwisselen, zoals zo: leegte swap (int * a, int * b); int tmp = * b; * b = * a; * a = tmp. Vergeet niet dat swaps argumenten nu pointers, en dus moeten we het adres van x-en y-pas in de oproep om te ruilen, zoals zo: swap (& x, & y). Dit nu correct swaps de waarden van x en y. Laten we trekken een box-and-pijldiagram waarom dit werkt. We beginnen met de twee dozen in geheugen x en y. Binnenkant van de doos voor x is het getal 4, en de binnenkant van de doos voor y hebben we 15. Nu, de binnenkant van de oproep tot de swap-functie, hebben we nog twee dozen voor de argumenten a en b; een wijst op de doos voor x, en b wijst op het vakje voor y. Een nieuwe doos wordt gemaakt voor de variabele tmp, en de binnenkant van het slaan wij het resultaat van dereferentie b, wat betekent 'de pijl volgen uit het vakje b.' Dus, we slaan 15 binnenkant van tmp. Dan volgen we de pijl aan b en slaan hier het resultaat van dereferentie een, Dit is de waarde 4. Tot slot volgen we de pijl op een en op te slaan wat op dit moment is de binnenkant van tmp, die is 15. Merk op dat de dozen gelabeld x-en y-waarden correct verwisseld. Zodra we meer leren over malloc en dynamisch geheugenbeheer, we zullen zien dat we geen andere keus hebben dan pointers te gebruiken. Wandelen door de box-and-pijl diagram voor elk programma kan u helpen erachter te komen wat het programma eigenlijk aan het doen is. Mijn naam is Rob Bowden, en dit is CS50. [CS50.TV] Dit is een andere toepassing voor het sterretje - bleah, ik haat dat woord. Overal hebben we gebruikt om alleen maar zeggen n, kunnen we nu zeggen pointer_to_n - nee je KAN NIET - * pointer_to_n.