[Powered by Google Translate] [Punters] [Rob Bowden] [Harvard University] [Aquesta és CS50] [CS50.TV] Anem a parlar sobre els punters. Fins ara, sempre hem acabem de referir-nos a les coses en la memòria explícitament pel seu nom. Hem dit int n = 42, i després quan volem utilitzar la variable n, simplement en diuen pel nom que li donem, escrivint alguna cosa com n * 2. Però aquesta variable ha de viure en algun lloc de la memòria. Si voleu utilitzar el valor que es troba actualment emmagatzemat a l'interior de n, o actualitzar el valor de n que es manté, el programa necessita saber on buscar a la memòria per an. Quan en la memòria viva una variable s'anomena la seva direcció. És com una adreça de la casa. Puc trobar la casa d'algú, sempre que sé que el seu domicili, i un programa d'ordinador pot trobar una variable sempre que es coneix la seva adreça de memòria. Què proporcionar punters és una manera de tractar directament amb aquestes adreces de memòria. Gran part de l'energia en C prové de la capacitat per manipular la memòria d'aquesta manera. Però amb gran poder comporta una gran responsabilitat. Els punters es pot utilitzar perillosament suficient que una gran quantitat de llenguatges de programació ocultar els punters del tot. Així que, per què C donar-nos consells, llavors? Com vostès saben, arguments a una funció C sempre es copien en els paràmetres de la funció. Per tant, dir alguna cosa així com intercanvi en algunes variables x i y no es poden intercanviar els valors de x i y en la funció que crida, tot i que podria ser útil. Com veurem més endavant, d'intercanvi de reescriptura per tenir punters als llocs que necessiten ser canviats li permet modificar les variables de la persona que truca. Anem a veure un exemple relativament senzill del que pot fer suggeriments. Diguem que tenim int n = 4, i = int * pointer_to_n & n. Whoa! Una mica de nova sintaxi per cobrir. En primer lloc, anem a interpretar això i núm. Recordi que tot en la memòria té alguna direcció. El signe s'anomena la "direcció de" operador. Així, i n es refereix a la direcció en memòria on s'emmagatzema núm. Ara, estem emmagatzemant aquesta direcció en una nova variable, pointer_to_n. Quin és el tipus d'aquesta nova variable? L'asterisc és una part del tipus de la variable, i anem a llegir el tipus com int *. Int * significa que pointer_to_n és una variable que emmagatzema la direcció d'un nombre enter. Sabem que & n és un int * ja que n és un enter, i estem prenent la direcció de n. Int * és un exemple d'un tipus de punter. Així que es comencen a veure els asteriscos al tipus, vostè sap que vostè està tractant amb punters. Igual que podem declarar una variable com int x i i char, podem dir int * z i char * w. * Int i char * són només nous tipus perquè poguéssim utilitzar. La ubicació del * pot anar enlloc abans que el nom de la variable. Així, tant int * pointer_to_n - amb la * a int, com ho hem fet aquí - i int pointer_to_n * amb el * al costat pointer_to_n són vàlids. Però aquí, vaig a posar el següent * a int. No importa el que vostè prefereix, acaba de ser consistent. Anem a dibuixar un diagrama per això. Primer tenim la variable n, que anem a dibuixar com una petita caixa de la memòria. Per aquest exemple, anem a dir que aquest quadre es troba a l'adreça 100. Dins d'aquest quadre, estem emmagatzemant el valor 4. Ara, tenim una nova variable, pointer_to_n. Té la seva pròpia caixa en la memòria, direm que està en la direcció 200. Dins d'aquest quadre, estem emmagatzemant la direcció de n, que abans deia era 100. Freqüents en els diagrames, podràs veure aquesta mostra com una fletxa literal portant-se a la pointer_to_n assenyalant la caixa que emmagatzema núm. Ara, què podem fer realment amb pointer_to_n? Doncs bé, si diem una mena * pointer_to_n = 8, aquest és un ús diferent per l'asterisc que és completament independent de la utilització de l'asterisc a declarar una variable d'un tipus de punter. En aquest cas, l'asterisc es diu l'operador d'indirecció. En el nostre diagrama, quin pointer_to_n * = 8 vol dir és, anar a la caixa que conté pointer_to_n, segueixi la fletxa, i després assignar a la caixa a l'extrem de la fletxa el valor 8. Això vol dir que després d'aquesta línia, si tractem d'usar n té el valor 8. 'Punter' La paraula s'usa en molts contextos diferents. Aquí, anem a tractar de ser consistent. Un tipus de punter és com * int. En aquest vídeo, un punter només s'utilitzen per designar un valor amb un tipus de punter, com pointer_to_n que té * tipus int. En qualsevol lloc que solia dir que només n, ara podem dir que en lloc pointer_to_n *, i tot funcionarà igual de bé. Anem a veure un altre exemple simple. Diguem que tenim int n = 14; int * punter = n + N + +, i (* punter) + +. La primera línia crea una nova caixa en la memòria de l'etiqueta núm. Aquesta vegada, no anem a etiquetar el quadre amb una adreça explícita, però encara té un. A l'interior de la caixa, que està emmagatzemant el número 14. La següent línia crea un segon punter quadre amb l'etiqueta. I dins d'aquest quadre, estem emmagatzemar un punter a la casella núm. Per tant, anem a dibuixar la fletxa de punter a n. Ara, n + + incrementa el valor a la casella núm, així que anem a partir de 14 a 15. Finalment, (* punter) + + va al punter quadre amb l'etiqueta, desreferencias el valor en la caixa, el que significa seguir la fletxa a on apunta, i incrementa el valor emmagatzemat aquí, així que anem a partir de 15 a 16. I això és tot. N ara emmagatzema el número 16 després d'haver estat incrementat dues vegades - una vegada directament amb els n nom de la variable, i l'altre a través d'un pointer_to_n. Prova ràpida. Què creus que vol dir que si jo tracte de dir alguna cosa com && n? Bé, anem a reescriure això com & (& n) que porta a terme la mateixa cosa. El (+ n) retorna la direcció de la variable n en la memòria. Però llavors signe extern intenta tornar la direcció de la direcció. Això és com tractar de fer i 2. No té sentit per obtenir la direcció de tan sols un nombre ja que no està sent emmagatzemat en la memòria. L'ús de dos símbols d'unió en una fila no és mai una bona idea. Però ara, què significa si tracte de dir int ** double_pointer = & punter? Ara, estic creant una nova caixa etiquetada double_pointer, i en l'interior d'aquesta caixa estic emmagatzemant la direcció de punter, el que significa que dibuixar una fletxa en el quadre de double_pointer a la caixa de punter. Tingueu en compte el tipus de double_pointer, un int **. N és un nombre enter, punter emmagatzema l'adreça de n, i pel que té * tipus int. Ara, double_pointer emmagatzema la direcció del punter, pel que és de tipus int. ** Així que, què és el que pensem que això significa - Double_pointer ** = 23? Recordeu que ara estic dereferencing dues vegades. Només has de seguir el diagrama de caixa i fletxa que ja hem establert. En primer lloc, anem a la casella double_pointer. * El primer mitjà de seguir la fletxa un cop. Ara, estem en el punter quadre amb l'etiqueta. La segona estrella diu seguir la fletxa de nou, i ara estem en la casella n, i establir el valor d'aquest quadre per 23. Tingueu en compte que la "direcció de" eliminar la referència i els operadors són inverses entre si. Això em permet fer alguna cosa com * & * & n = 42. Encara que això funciona, vostè mai ha de fer alguna cosa com això en la pràctica. Què estem fent aquí? El signe primer agafa la direcció de la variable n. Llavors, tenim un operador per desfer referències, el que significa que anem a aquesta adreça a la memòria, així que estem de tornada al n. Ara, agafa la direcció de n de nou i eliminar la referència immediata, així que estem de tornada en n i botiga 42. Així, cada parell de * i només s'anul. Hi ha un punter especial anomenat el punter nul. Això és un punter que mai hauria d'eliminar la referència. Aquest indicador és important perquè ens dóna una manera de distingir entre un punter que ha i no ha de deixar de fer referència. Si intenta eliminar la referència a un punter nul, normalment el programa es bloquejarà amb un error de segmentació, que és possible que hagi vist abans. Així que, diguem que tenim el codi int * x = null; * x = 4. En aquest exemple, pot semblar obvi que estem fent alguna cosa malament, però recordi que null realment podria ser un valor retornat per una crida a una funció com malloc, si malloc no pot assignar la memòria sol · licitada per l'usuari. Per això, si per contra ens fixem el valor de x d'una crida a malloc, com a int * x = malloc (sizeof (int)), llavors sempre cal comprovar explícitament per veure si nul va ser retornat. Si (x == null) / / uhoh! retorn, més que puguem seguir endavant i dir * x = 4. Així que, de nou, per què ens utilitzen punters? Vegem un exemple d'un programa en què hem d'utilitzar punters - una funció d'intercanvi simple. Diguem que tinc dos nombres enters, int x = 4, i int i = 15; i vull escriure una funció anomenada swap que puc utilitzar d'aquesta manera: swap (x, i). Després d'aquesta línia, els valors dins de la variable x ha de ser de 15, i el valor de la variable i en l'interior ha de ser de 4. Els valors dins de x i i s'han intercanviat. Sense punters, podríem intentar alguna cosa com intercanvi void (int a, int b); int tmp = b, b = a, a = tmp. Però, t'adones el problema amb això? Recordeu que el valor emmagatzemat en la variable a és simplement una còpia del valor de x, i el valor de b es copia de i. Qualsevol canvi realitzat en a i b no es reflectirà en x i y. Així, mentre que els valors de a i b s'intercanvien correctament, x i i no han canviat en absolut. Ara, anem a canviar la funció d'intercanvi de manera que els seus arguments són punters a les variables que han de bescanviar, d'aquesta manera: intercanvi void (int * a, int * b); int tmp = b *, * = b * a, * a = tmp. Recordeu que els arguments de swaps són ara punters, i el que hem de passar la direcció de x i i en la crida a canviar, d'aquesta manera: intercanviar (& x, & i). Això ara correctament intercanvia els valors de x i y. Anem a dibuixar un diagrama de caixa i fletxa per veure què funciona això. Comencem amb els nostres dos caixes en la memòria, x i i. A l'interior de la caixa perquè x és el número 4, i en l'interior de la caixa per i tenim 15. Ara, dins de la crida a la funció d'intercanvi, tenim dues caixes més per als arguments a i b; punts a la caixa per x, i els punts b al quadre per i. Una nova caixa es crea per al tmp variable, i dins d'ella es guarda el resultat de l'eliminació de referències b, que significa "seguir la fletxa de la casella b. Per tant, emmagatzemem 15 en l'interior de tmp. Després, segueix la fletxa en B i emmagatzemar aquí el resultat d'una eliminació de referències, que és el valor 4. Finalment, seguim la fletxa en una botiga i el que és en l'actualitat dins de tmp, que és de 15. Recordeu que les caixes etiquetades x i i han canviat els valors correctament. Quan aprenem més sobre malloc i la gestió de memòria dinàmica, veurem que no tenim més remei que utilitzar punters. Caminant pel diagrama de caixa i fletxa per qualsevol programa pot ajudar a esbrinar el que el programa està fent en realitat. El meu nom és Rob Bowden, i això és CS50. [CS50.TV] Aquest és un ús diferent per l'asterisc - bleah, odio aquesta paraula. En qualsevol lloc que solia dir que només n, ara podem dir pointer_to_n - no et puc - pointer_to_n *.