[Powered by Google Translate] [§ 4 - Mer komfortabelt]
[Rob Bowden - Harvard University]
[Dette er CS50. - CS50.TV]
Vi har en quiz i morgen, i tilfelle dere ikke visste det.
Det er i utgangspunktet på alt du kunne ha sett i klassen eller burde ha sett i klassen.
Det inkluderer pekere, selv om de er en svært nylig tema.
Du bør i det minste forstå de høye nivåene av dem.
Noe som ble gått over i klassen du bør forstå for quiz.
Så hvis du har spørsmål om dem, kan du be dem nå.
Men dette kommer til å bli en svært student-ledede session hvor dere stille spørsmål,
så forhåpentligvis folk har spørsmål.
Har noen spørsmål?
Ja. >> [Student] Kan du gå over pekere igjen?
Jeg skal gå over pekere.
Alle dine variabler nødvendigvis lever i minnet,
men vanligvis du ikke bekymre deg for det, og du bare si x + 2 og y + 3
og kompilatoren vil finne ut hvor ting lever for deg.
Når du arbeider med pekere, nå er du eksplisitt bruker disse minne adresser.
Slik at en enkelt variabel vil bare noensinne leve på en enkelt adresse til enhver tid.
Hvis vi ønsker å erklære en peker, hva slags kommer til å se ut?
>> Jeg ønsker å erklære en peker p. Hvordan ser den typen ut?
[Student] int * p. >> Ja. Så int * p.
Og hvordan gjør jeg det peke x? >> [Student] Ampersand.
[Bowden] Så ampersand er bokstavelig talt kalles adressen til operatør.
Så når jeg sier og x det blir minnet adressen til variabelen x.
Så nå har jeg pekeren p, og hvor som helst i koden min kan jeg bruke * p
eller jeg kunne bruke x og det vil være akkurat det samme.
(* P). Hva er dette å gjøre? Hva betyr den stjernen?
[Student] Det betyr en verdi på det tidspunktet. >> Ja.
Så hvis vi ser på det, kan det være svært nyttig å trekke ut diagrammer
der dette er en liten boks minne for x, som tilfeldigvis har verdien 4,
så har vi en liten boks minne for p,
og så p peker på x, så vi trekker en pil fra p til x.
Så når vi sier * p vi sier gå til boksen som er p.
Star er følge pilen og deretter gjøre hva du vil med den boksen der.
Så jeg kan si * p = 7, og som vil gå til boksen som er x og forandring som til 7.
Eller jeg kan si int z = * p * 2, det er forvirrende fordi det er star, stjerne.
Den ene stjerne er dereferencing p, den andre stjerne multiplisere med 2.
Legg merke til at jeg kunne ha like godt erstattet * p med x.
Du kan bruke dem på samme måte.
Og så senere jeg kan ha p peker til en helt ny ting.
Jeg kan bare si p = &z;
Så nå P ikke lenger peker til x, det peker til z.
Og hver gang jeg gjør * p er det samme som å gjøre z.
Så nyttige ting om dette er når vi begynner å få inn funksjoner.
>> Det er litt nytteløst å erklære en peker som peker til noe
og da er du bare dereferencing det
når du kunne ha brukt den opprinnelige variabelen til å begynne med.
Men når du kommer inn funksjoner - så la oss si at vi har noen funksjon, int foo,
som tar en peker og bare gjør * p = 6;
Som vi har sett før med swap, kan du ikke gjøre en effektiv swap og en egen funksjon
ved bare passerer heltall fordi alt i C er alltid bestått av verdi.
Selv når du passerer pekere du passerer verdi.
Det bare så skjer at disse verdiene er minneadresser.
Så når jeg sier foo (p), jeg har bestått pekeren inn i funksjonen Foo
og deretter Foo gjør * p = 6;
Så innsiden av den funksjonen, er * p fremdeles tilsvarende x,
men jeg kan ikke bruke x innsiden av den funksjonen fordi det ikke scoped innenfor denne funksjonen.
Så * p = 6 er den eneste måten jeg kan få tilgang til en lokal variabel fra en annen funksjon.
Eller, vel, pekere er den eneste måten jeg kan få tilgang til en lokal variabel fra en annen funksjon.
[Student] La oss si at du ønsket å returnere en peker. Hvor nøyaktig gjør du det?
[Bowden] Tilbake en peker som i noe sånt int y = 3; retur & y? >> [Student] Yeah.
[Bowden] Okay. Du bør aldri gjøre dette. Dette er ille.
Jeg tror jeg så i disse foredrag lysbilder du begynte å se hele denne diagram av minne
der opp her har du minne adresse 0
og ned her har du minneadresser 4 konserter eller 2 til 32.
Så da har du noen ting og noen ting og da har du din stabel
og du har fått din haug, som du nettopp begynt å lære om, vokser opp.
[Student] Er ikke haugen over stabelen?
>> Ja. Haugen er på toppen, er det ikke? >> [Student] Vel, la han 0 på toppen.
[Student] Oh, la han 0 på toppen. >> [Student] Oh, okay.
Ansvarsfraskrivelse: Anywhere med CS50 du kommer til å se det på denne måten. >> [Student] Okay.
Det er bare det at når du først ser stabler,
som når du tenker på en stabel du tenker på å stable ting på toppen av hverandre.
Så vi har en tendens til å snu dette rundt slik stabelen vokser opp som en stabel normalt ville
istedenfor bunken hengende ned. >> [Student] Ikke masser teknisk vokse opp også, skjønt?
Det kommer an på hva du mener med å vokse opp.
Bunken og heap alltid vokse i motsatt retning.
En stabel er alltid vokser opp i den forstand at det vokser opp
mot høyere minne adresser, og haugen vokser ned
i at det vokser mot lavere minneadresser.
Slik at toppen er 0, og bunnen er høye fysiske adresser.
De er begge vokser, bare i motsatt retning.
[Student] Jeg mente at fordi du sa at du setter stabelen på bunnen
fordi det virker mer intuitivt fordi for bunken for å starte på toppen av en haug,
heap er på toppen av seg selv også, så that - >> Ja.
Du også tenke på haugen som vokser opp og større, men bunken mer.
Så stabelen er den som vi på en måte ønsker å vise oppveksten.
Men overalt hvor du ser ellers kommer til å vise adresse 0 øverst
og det høyeste minne adressen nederst, så dette er din vanlige syn på minne.
>> Har du et spørsmål?
[Student] Kan du fortelle oss mer om haugen?
Ja. Jeg får til det i et sekund.
Først kommer tilbake til hvorfor retur og y er en dårlig ting,
på stakken har du en haug av stabelen rammer som representerer alle funksjoner
som har blitt kalt.
Så ignorerer tidligere ting, er toppen av stabelen din alltid kommer til å være den viktigste funksjonen
siden det er den første funksjonen som blir kalt.
Og når du ringer opp en annen funksjon, stabelen kommer til å vokse ned.
Så hvis jeg kaller noen funksjon, foo, og det får sin egen stack frame,
den kan kalle noen funksjon, bar, det får sin egen stack ramme.
Og bar kan være rekursiv, og det kan kalle seg selv,
og slik at andre kall til bar kommer til å få sin egen stack ramme.
Og så hva som foregår i disse stabel rammer er alle lokale variabler
og alle funksjonsargumenter at -
Noen ting som er lokalt scoped til denne funksjonen gå i disse stabel rammer.
Så det betyr at når jeg sa noe sånt som bar er en funksjon,
Jeg bare kommer til å erklære et heltall og deretter returnere en peker til denne heltall.
Så hvor bor y?
[Student] y bor i baren. >> [Bowden] Yeah.
Et sted i denne lille plassen minne er en littler firkant som har y i den.
Når jeg kommer tilbake og y, er jeg tilbake en peker til denne lille blokk med minne.
Men så når en funksjon returnerer, får sin stack ramme popped av stabelen.
Og det er derfor det kalles stabelen.
Det er som stabelen datastrukturen, hvis du vet hva det er.
Eller selv som en stabel av skuffene er alltid eksemplet
main kommer til å gå på bunnen, så den første funksjonen du ringe kommer til å gå på toppen av det,
og du ikke kan komme tilbake til hovedsiden til du kommer tilbake fra alle funksjoner som har blitt kalt
som har blitt plassert på toppen av den.
>> [Student] Så hvis du gjorde gjøre returnere & y, at verdien kan endres uten varsel.
Ja, det er - >> [student] Det kan bli overskrevet. >> Ja.
Det er helt - Hvis du prøver og -
Dette vil også være en int * bar fordi den returnerer en peker,
så det returtype er int *.
Hvis du prøver å bruke returverdien av denne funksjonen, er det udefinert oppførsel
fordi det peker i dårlig hukommelse. >> [Student] Okay.
Så hva om, for eksempel, erklærte du int * y = malloc (sizeof (int))?
Det er bedre. Ja.
[Student] Vi snakket om hvordan når vi drar ting til vår papirkurven
de er faktisk ikke slettet, vi bare miste sine pekere.
Så i dette tilfellet gjør vi faktisk slette verdien eller er det fortsatt det i minnet?
For det meste, det kommer til å fortsatt være der.
Men la oss si at vi tilfeldigvis ringe noen annen funksjon, Baz.
Baz kommer til å få sin egen stack ramme på her.
Det kommer til å bli overskriving alt dette ting,
og hvis du senere prøve og bruke pekeren som du fikk før,
det er ikke til å være den samme verdi.
Det kommer til å ha endret bare fordi du ringte funksjonen baz.
[Student] Men hadde vi ikke, ville vi fortsatt få 3?
[Bowden] I all sannsynlighet, ville du.
Men du kan ikke stole på det. C sier bare udefinert oppførsel.
>> [Student] Oh, det gjør det. Okay.
Så når du ønsker å returnere en peker, dette er hvor malloc kommer i bruk.
Jeg skriver egentlig bare returnere malloc (3 * sizeof (int)).
Vi vil gå igjennom malloc mer i et sekund, men ideen om malloc er alle dine lokale variabler
alltid gå på stakken.
Noe som er malloced går på haugen, og det vil for evig og alltid være på haugen
før du eksplisitt frigjøre den.
Så dette betyr at når du malloc noe, det kommer til å overleve etter at funksjonen returnerer.
[Student] Vil det overleve etter at programmet slutter å kjøre? >> Nei
Ok, så det kommer til å være der til programmet er helt ferdig å kjøre. >> Ja.
Vi kan gå over detaljer om hva som skjer når programmet stopper.
Du må kanskje minne meg, men det er en egen ting helt.
[Student] Så malloc skaper en peker? >> Ja.
Malloc - >> [student] Jeg tror malloc betegner en blokk med minne som en peker kan bruke.
[Bowden] Jeg vil at diagrammet igjen. >> [Student] Så denne funksjonen fungerer, skjønt?
[Student] Yeah, malloc betegner en blokk med minne som du kan bruke,
og da er det returnerer adressen til den første blokken i dette minnet.
>> [Bowden] Yeah. Så når du malloc, du flytte noen minneblokk
det er for tiden i haugen.
Hvis haugen er for liten, så haugen er bare kommer til å vokse, og det vokser i denne retningen.
Så la oss si haugen er for liten.
Da er det i ferd med å vokse litt og returnere en peker til denne blokken som bare vokste.
Når du gratis ting, du gjør mer plass i haugen,
så da senere ringer til malloc kan gjenbruke at minnet som du hadde tidligere frigjort.
Det viktige ting om malloc og gratis er at det gir deg full kontroll
over levetiden for disse minneblokker.
Globale variabler er alltid live.
Lokale variabler er i live innenfor sitt omfang.
Så snart du går forbi en krøllete brace, lokale variabler er døde.
Malloced minne er i live når du vil den skal være i live
og deretter frigjøres når du forteller det til å bli utgitt.
De er faktisk de eneste tre typer minne, egentlig.
Det er automatisk minnehåndtering, som er bunken.
Ting skjer for deg automatisk.
Når du sier int x, er minne for int x.
Når x går ut av omfanget, er minnet gjenvunnet til x.
Så er det dynamisk minnehåndtering, som er hva malloc er,
som er når du har kontroll.
Du dynamisk bestemme når minne bør og ikke bør tildeles.
Og så er det statisk, noe som betyr bare at det lever evig,
som er det globale variabler er.
De er bare alltid i minnet.
>> Spørsmål?
[Student] Kan du definere en blokk bare ved hjelp av klammeparentes
men ikke å måtte ha en hvis setningen eller en stund uttalelse eller noe sånt?
Du kan definere en blokk som i en funksjon, men som har klammeparentes også.
[Student] Så du kan ikke bare ha som en tilfeldig par klammeparentes i koden
som har lokale variabler? >> Ja, det kan du.
Innsiden av int bar kunne vi ha {int y = 3;}.
Som er ment å være her.
Men som definerer fullstendig omfanget av int y.
Etter at andre krøllete brace, kan y ikke brukes lenger.
Du nesten aldri gjøre det, though.
Komme tilbake til hva som skjer når et program slutter,
Det er litt av en misforståelse / halv løgn at vi gir for å bare gjøre ting enklere.
Vi forteller deg at når du allokere minne
du fordele noen del av RAM for den variabelen.
Men du er ikke egentlig direkte berøre RAM noensinne i programmene dine.
Hvis du tenker på det, hvordan jeg trakk -
Og faktisk, hvis du går gjennom i GDB vil du se det samme.
Uavhengig av hvor mange ganger du kjører programmet eller hvilket program du kjører,
stabelen alltid skal starte -
du alltid kommer til å se variabler rundt adresse oxbffff noe.
Det er vanligvis et sted i den regionen.
Men hvordan kan to programmer muligens ha pekere til samme minne?
[Student] Det er en oppkonstruert utpeking av hvor oxbfff er ment å være på RAM
som faktisk kan være på forskjellige steder avhengig av når funksjonen ble kalt.
Ja. Begrepet er virtuelt minne.
Ideen er at hver enkelt prosess, hver eneste program som kjører på datamaskinen
har sin egen - la oss anta 32 bits - helt uavhengig adresse plass.
Dette er adressen plass.
Det har sine egne helt uavhengig 4 gigabyte å bruke.
>> Så hvis du kjører to programmer samtidig, ser dette programmet 4 gigabyte til seg selv,
dette programmet ser 4 gigabyte til seg selv,
og det er umulig for dette programmet å dereference en peker
og ender opp med minne fra dette programmet.
Og hva virtuelt minne er en kartlegging fra en prosesser adresse plass
til faktiske ting på RAM.
Så det er opp til operativsystemet for å vite det,
hei, når denne fyren dereferences pekeren oxbfff, som egentlig betyr
at han ønsker RAM byte 1000,
mens hvis dette programmet dereferences oxbfff, ønsker han virkelig RAM byte 10000.
De kan være vilkårlig langt fra hverandre.
Dette er også sant av ting innenfor en enkelt prosesser adresse plass.
Så ut som det ser alle fire gigabyte til seg selv, men la oss si -
[Student] Har hver enkelt prosess -
La oss si du har en datamaskin med bare 4 gigabyte RAM.
Ser hver enkelt prosess hele 4 gigabyte? >> Ja.
Men de fire gigabyte det ser er en løgn.
Det er bare den tror den har alt dette minnet fordi den ikke vet noen annen prosess eksisterer.
Det vil bare bruke så mye minne som det faktisk er behov for.
Operativsystemet er ikke tenkt å gi RAM til denne prosessen
hvis det ikke bruker minnet på dette hele regionen.
Det kommer ikke til å gi den minne for regionen.
Men ideen er at - Jeg prøver å tenke på - jeg kan ikke tenke på en analogi.
Analogier er vanskelig.
Ett av problemene virtuelt minne eller en av de tingene det er å løse
at prosesser skal være helt uvitende om hverandre.
Og så kan du skrive et program som bare dereferences noen peker,
liker bare skrive et program som sier * (ox1234),
og det er dereferencing minneadresse 1234.
>> Men det er opp til operativsystemet for å deretter oversette hva 1234 betyr.
Så hvis 1234 skjer for å være en gyldig minne adresse for denne prosessen,
som det er på stakken eller noe, da dette vil returnere verdien av at minneadresse
så langt som prosessen vet.
Men hvis 1234 er ikke en gyldig adresse, som det skjer til land
i noen lite stykke minne her som er utenfor stabelen og utover haugen
og du har egentlig ikke brukt det, så det er da du får ting som segfaults
fordi du berører minne som du ikke bør berøre.
Dette er også tilfelle -
En 32-bits system, betyr 32 biter du har 32 bits for å definere et minne adresse.
Det er derfor pekere er 8 byte fordi 32 bits er 8 byte - eller 4 byte.
Pekere er 4 byte.
Så når du ser en peker som oxbfffff, er at -
Innenfor et gitt program kan du bare lage en vilkårlig pekeren,
alt fra ox0 til okse 8 f's - FFFFFFFF.
[Student] Sa du ikke at de er 4 byte? >> Ja.
[Student] Så hver byte vil ha - >> [Bowden] Heksadesimal.
Heksadesimal - 5, 6, 7, 8. Så pekere du kommer til å alltid se i heksadesimal.
Det er bare hvordan vi klassifiserer pekere.
Hver 2 sifrene i heksadesimal er 1 byte.
Så det kommer til å bli 8 heksadesimale sifre for 4 bytes.
Så hver eneste peker på en 32-bit system skal være 4 byte,
noe som betyr at i prosessen kan du konstruere vilkårlige 4 byte
og foreta en peker ut av det,
noe som betyr at så langt som det er klar, kan det ta en hel 2 til 32 byte minne.
Selv om det ikke egentlig har tilgang til den,
selv om datamaskinen bare har 512 megabyte, mener det det har så mye minne.
Og operativsystemet er smart nok til at det vil bare tildele hva du faktisk trenger.
Det betyr ikke bare gå, oh, en ny prosess: 4 konserter.
>> Ja. >> [Student] Hva betyr oksen? Hvorfor skriver du det?
Det er bare symbolet for heksadesimal.
Når du ser et tall start med okse, de påfølgende ting er heksadesimal.
[Student] Du skulle forklare om hva som skjer når et program slutter. >> Ja.
Hva skjer når et program slutter er operativsystemet
bare sletter kartlegginger som den har for disse adressene, og det er det.
Operativsystemet kan nå bare gi som minne til et annet program å bruke.
[Student] Okay.
Så når du tildeler noe på haugen eller stable eller globale variabler eller noe,
de bare forsvinner så snart programmet avsluttes
fordi operativsystemet er nå fri til å gi den minne til andre prosesser.
[Student] Selv om det er sannsynligvis fortsatt verdier skrevet i? >> Ja.
Verdiene er sannsynligvis fortsatt der.
Det er bare det kommer til å være vanskelig å få på dem.
Det er mye vanskeligere å få på dem enn det er å få på en slettet fil
fordi den slettede filen slags sitter der i lang tid, og harddisken er mye større.
Så det kommer til å overskrive ulike deler av minnet
før det skjer for å overskrive del av minnet som filen pleide å være på.
Men hovedminne, RAM, du sykle gjennom mye raskere,
så det kommer til veldig raskt bli overskrevet.
Spørsmål om dette eller noe annet?
[Student] Jeg har spørsmål om et annet tema. >> Ok.
Har noen spørsmål om dette?
>> Okay. Annet tema. >> [Student] Okay.
Jeg gikk gjennom noen av de praktiske testene,
og i en av dem ble det snakket om sizeof
og verdien som den returnerer eller ulike variable typer. >> Ja.
Og det sies at både int og lang både avkastning 4, slik at de er begge 4 byte.
Er det noen forskjell mellom en int og en lang, eller er det det samme?
Ja, det er en forskjell.
C-standard -
Jeg sannsynligvis kommer til å rote.
C-standarden er akkurat hva C er, den offisielle dokumentasjonen av C.
Dette er hva den sier.
Så C standarden sier bare at en røye vil for evig og alltid være en byte.
Alt etter som - en kort er alltid bare definert som å være større enn eller lik en char.
Dette kan være strengt større enn, men ikke positiv.
En int er bare definert som å være større enn eller lik en kort.
Og en lang er bare definert som å være større enn eller lik en int.
Og en lang lang er større enn eller lik en lang.
Så det eneste C-standarden definerer er den relative bestilling av alt.
Den faktiske mengden minne om at ting tar opp er vanligvis opp til implementering,
men det er ganske godt definert på dette punktet. >> [Student] Okay.
Så shorts er nesten alltid kommer til å være to bytes.
Ints er nesten alltid kommer til å være 4 byte.
Lange lengter nesten alltid kommer til å være 8 byte.
Og lengter, det avhenger av om du bruker en 32-bit eller 64-bit system.
Så lenge skal tilsvare den type system.
Hvis du bruker en 32-bit system som Appliance, kommer det til å være 4 byte.
Hvis du bruker en 64-bit like mye nyere datamaskiner, kommer det til å være 8 byte.
>> Ints er nesten alltid 4 bytes på dette punktet.
Lange lengter er nesten alltid 8 byte.
I det siste, brukte ints å bare være to bytes.
Men merker at dette helt tilfredsstiller alle disse relasjoner større enn og lik.
Så lenge er perfekt lov til å være av samme størrelse som et heltall,
og det er også lov til å være av samme størrelse som en lang lang.
Og det bare så skjer for å være det i 99,999% av systemer, er det kommer til å være lik
enten en int eller en lang lang. Det avhenger bare av 32-bit eller 64-bit. >> [Student] Okay.
I flyter, hvordan er desimaltegn utpekt i form av bits?
Liker som binære? >> Ja.
Du trenger ikke å vite at for CS50.
Du trenger ikke engang vite at i 61.
Du trenger ikke lære at virkelig i et kurs.
Det er bare en representasjon.
Jeg glemmer den eksakte bit parseller.
Ideen om flyttall er at du tildele et bestemt antall biter til å representere -
I utgangspunktet er alt i vitenskapelig notasjon.
Så du tildele et bestemt antall biter til å representere selve nummeret, som 1,2345.
Jeg kan aldri representere et tall med flere siffer enn 5.
Da har du også sette av et bestemt antall biter, slik at det har en tendens til å være like
du kan bare gå opp til et visst antall, som det er den største eksponenten du kan ha,
og du kan bare gå ned til en viss eksponent,
sånn er det minste eksponenten du kan ha.
>> Jeg husker ikke nøyaktig hvordan bitene tildelt alle disse verdiene,
men et visst antall biter er dedikert til 1,2345,
en annen visst antall bits er dedikert til eksponent,
og det er bare mulig å representere en eksponent av en viss størrelse.
[Student] Og en dobbel? Er at som en ekstra lang float? >> Ja.
Det er det samme som en dupp bortsett fra nå du bruker 8 byte i stedet for 4 byte.
Nå vil du kunne bruke 9 sifre eller 10 sifre,
og dette vil være i stand til å gå opp til 300 i stedet for 100. >> [Student] Okay.
Og flyter finnes også 4 bytes. >> Ja.
Vel, igjen, det avhenger sannsynligvis samlet på generell implementering,
men flyter er 4 byte, dobler er 8.
Dobler kalles dobbelt fordi de er dobbelt så stort som flyter.
[Student] Okay. Og er det dobler dobbelt? >> Det er det ikke.
Jeg tror - >> [student] Liker lange lengter? >> Ja. Jeg tror ikke det. Ja.
[Student] På fjorårets test var det et spørsmål om den viktigste funksjonen
å være en del av programmet.
Svaret var at det ikke trenger å være en del av programmet.
I hvilken situasjon? Det er det jeg så.
[Bowden] Det virker - >> [student] Hvilken situasjon?
Har du problemet? >> [Student] Ja, jeg kan definitivt dra det opp.
Det trenger ikke å være, teknisk, men i utgangspunktet er tenkt å være.
[Student] Jeg så en på en annen årets.
Det var som Sant eller usant: Et gyldig - >> Å, en c-fil.?
. [Student] Enhver c filen må ha - [både tale på en gang - uforståelig]
Okay. Så det er separate.
>> A. C-fil trenger bare å inneholde funksjoner.
Du kan kompilere en fil til maskinkode, binær, uansett,
uten at det er kjørbar ennå.
En gyldig kjørbar må ha en hovedfunksjon.
Du kan skrive 100 funksjoner i en fil, men ingen viktigste
og deretter kompilere det ned til binær,
Så skriver du en annen fil som bare har main men det kaller en haug av disse funksjonene
i denne binærfil over her.
Og så når du gjør den kjørbare, det er hva det linker gjør
er det kombinerer disse to binære filer i en kjørbar.
Så en. C-fil ikke trenger å ha en hovedfunksjon i det hele tatt.
Og på store kode baser vil du se tusenvis av. C-filer og en main fil.
Flere spørsmål?
[Student] Det var et annet spørsmål.
Det sa gjør er en kompilator. Sant eller usant?
Og svaret var falsk, og jeg forsto hvorfor det er ikke som Clang.
Men hva kaller vi gjør hvis det ikke?
Gjøre er i utgangspunktet bare - jeg kan se nøyaktig det de kaller det.
Men det går bare kommandoer.
Gjør.
Jeg kan trekke dette opp. Ja.
Oh, yeah. Gjør også gjør det.
Dette sier hensikten med make-verktøyet er å bestemme automatisk
der deler av et stort program som trenger en ny
og utstede kommandoer å rekompilere dem.
Du kan lage lage filer som er helt enorm.
Gjør ser på tidsangivelser for filer og, som vi sa før,
du kan kompilere individuelle filer ned, og det er ikke før du kommer til linker
at de er satt sammen til en kjørbar.
Så hvis du har 10 forskjellige filer og gjøre en endring i en av dem,
så hva gjør kommer til å gjøre er bare rekompilere at en fil
og deretter koble alt sammen.
Men det er mye dummere enn det.
Det er opp til deg å fullstendig definere at det er hva det skal gjøre.
Det som standard har evnen til å gjenkjenne denne tidsangivelsen ting,
men du kan skrive en make-fil for å gjøre noe.
Du kan skrive en make-fil slik at når du skriver at det bare cd-er til en annen katalog.
Jeg var frustrert fordi jeg takle alt inne i Appliance min
og da jeg vise PDF-filen fra Mac.
>> Så jeg går til Finder og jeg kan ikke gå, koble til serveren,
og serveren jeg kobler til er min Appliance, og da jeg åpner opp PDF
som blir utarbeidet av LaTeX.
Men jeg begynte å bli frustrert fordi hver eneste gang jeg trengte å oppdatere PDF,
Jeg måtte kopiere den til en bestemt katalog at det kunne få tilgang
og det var å få irriterende.
Så i stedet skrev jeg en make-fil, som du må definere hvordan det gjør ting.
Hvordan du gjør i denne er PDF LaTeX.
Akkurat som alle andre lag file - eller jeg antar at du ikke har sett gjøre filer,
men vi har i Appliance en global lag fil som bare sier,
Hvis du kompilere et C-fil, kan du bruke Clang.
Og så her i min make-fil som jeg gjør jeg si,
denne filen du skal ønske å kompilere med PDF LaTeX.
Og så er det PDF LaTeX som gjør det kompilering.
Gjør ikke kompilering. Det er bare å kjøre disse kommandoene i den rekkefølgen jeg spesifisert.
Så det går PDF LaTeX, kopierer det opp til katalogen jeg ønsker det skal kopieres til,
det cd-er til katalogen og gjør andre ting,
men alt det gjør er gjenkjenne når en fil endres,
og hvis den endres, så vil det kjøre kommandoene som det er ment å kjøre
Når filen endres. >> [Student] Okay.
Jeg vet ikke hvor de globale gjøre filer er for meg å sjekke det ut.
Andre spørsmål? Alt fra fortiden quizer? Eventuelle pekeren ting?
Det er subtile ting med pekere som -
Jeg har ikke tenkt å være i stand til å finne en quiz spørsmål om det -
men akkurat som denne typen ting.
Sørg for at du forstår at når jeg sier int * x * y -
Dette er ikke akkurat noe her, antar jeg.
Men som * x * y, de er to variabler som er på stakken.
Når jeg sier x = malloc (sizeof (int)), x fortsatt en variabel på stakken,
malloc er noen blokk over i haugen, og vi har x peker på haugen.
>> Så noe på stakken peker på haugen.
Når du malloc noe, er du nødvendigvis lagre den på innsiden av en peker.
Slik at pekeren er på stakken, er malloced blokk på haugen.
Mange mennesker får forvirret og si int * x = malloc, x er på haugen.
Nei Hva x peker til er på haugen.
x selv er på stakken, med mindre en eller annen grunn være deg x har en global variabel,
i hvilket tilfelle det skjer for å være i en annen region av minnet.
Så holde orden, disse boks og pil diagrammer er ganske vanlig for quiz.
Eller hvis det ikke er på quiz 0, vil det være på quiz en.
Du bør vite alle disse, trinnene i kompilering
siden du måtte svare på spørsmål om disse. Ja.
[Student] Kan vi gå over disse trinnene - >> Sure.
Før trinn og kompilere har vi forbehandling,
kompilering, montering, og linking.
Forbehandling. Hva gjør det?
Det er den enkleste skritt i - vel, ikke som -
det betyr ikke at det bør være opplagt, men det er den enkleste trinnet.
Dere kan gjennomføre det selv. Ja.
[Student] Ta det du har i din inneholder som dette, og det kopierer og deretter også definerer.
Det ser for ting som # include og # definere,
og det bare kopierer og limer hva de egentlig mener.
Så når du sier # include cs50.h er preprosessor kopiere og lime cs50.h
inn i den linjen.
Når du sier # define x å være 4, går preprosessor gjennom hele programmet
og erstatter alle forekomster av x med 4.
Så preprosessor tar en gyldig C-fil og utganger en gyldig C-fil
hvor ting har blitt kopiert og limt inn.
Så nå kompilering. Hva gjør det?
[Student] Det går fra C til binær.
>> [Bowden] Det går ikke hele veien til binær.
[Student] til maskinkode da? >> Det er ikke maskinkode.
[Student] Assembly? >> Assembly.
Det går til Assembly før det går hele veien til C-kode,
og de fleste språk gjøre noe som dette.
Plukke noen høyt nivå språk, og hvis du kommer til å kompilere den,
det er sannsynlig å kompilere i trinn.
Først det kommer til å kompilere Python til C, så det kommer til å kompilere C til Assembly,
og deretter Montering kommer til å bli oversatt til binær.
Så kompilere kommer til å bringe den fra C til forsamlingen.
Ordet kompilering betyr vanligvis bringe det fra et høyere nivå
til et lavere nivå programmeringsspråk.
Så dette er den eneste skritt i kompilering der du starter med et høyt nivå språk
og ender opp i en lav-nivå språk, og det er derfor trinnet kalles kompilering.
[Student] Under kompilering, la oss si at du har gjort # include cs50.h.
Vil kompilatoren rekompilere cs50.h, som funksjonene som er der inne,
og oversette det til Assembly kode i tillegg,
eller vil den kopiere og lime noe som har vært pre-Assembly?
cs50.h vil stort sett aldri ende opp i Assembly.
Ting som funksjon prototyper og ting er bare for deg å være forsiktig.
Det garanterer at kompilatoren kan sjekke ting som du ringer funksjoner
med riktig retur typer og de riktige argumenter og sånt.
>> Så cs50.h vil bli preprocessed i filen, og deretter når det er kompilering
det er i utgangspunktet kastet bort etter at det sørger for at alt kalles riktig.
Men funksjonene som er definert i CS50 biblioteket, som er atskilt fra cs50.h,
de vil ikke bli særskilt kompilert.
Som faktisk vil komme ned i å knytte skritt, så vi får til det i et sekund.
Men først, hva er montering?
[Student] Assembly til binære? >> Ja.
Montering.
Vi kaller ikke det kompilering fordi Assembly er ganske mye en ren oversettelse av binære.
Det er svært lite logikk i å gå fra Assembly til binær.
Det er akkurat som å se opp i en tabell, oh, har vi denne instruksjonen;
som tilsvarer binær 01110.
Og slik at filene som montering generelt utganger er. O-filer.
Og. O-filer er hva vi sier før,
hvordan en fil ikke trenger å ha en hovedfunksjon.
Hvilken som helst fil kan kompileres ned til en. O fil så lenge det er en gyldig C-fil.
Det kan kompileres ned til. O.
Nå er knytte hva som faktisk bringer en haug med. O filer og bringer dem til en kjørbar.
Og så hva linking gjør er at du kan tenke på CS50 biblioteket som en. O-fil.
Det er en allerede kompilert binærfil.
Og så når du kompilere filen, din hallo.c, som kaller GetString,
hallo.c blir utarbeidet ned til hello.o,
hello.o er nå i binær.
Den bruker GetString, så det må gå over til cs50.o,
og linker smooshes dem sammen og kopierer GetString inn i denne filen
og kommer ut med en kjørbar som har alle funksjoner det er behov for.
Så cs50.o er faktisk ikke en O-fil, men det er nært nok til at det ikke er noen grunnleggende forskjell.
Så knytte bare bringer en haug med filer sammen
som hver for seg inneholder alle de funksjoner jeg trenger å bruke
og skaper den kjørbare som faktisk vil kjøre.
>> Og så det er også hva vi sier før
der du kan ha 1000. c-filer, kompilere du dem alle til. o-filer,
som trolig vil ta en stund, så endrer en. c-fil.
Du trenger bare å rekompilere at 1. C-fil og deretter koble alt annet,
koble alt sammen igjen.
[Student] Når vi kobler vi skriver lcs50?
Ja, så-lcs50. Det flagget signaler til linker som du bør knytte i biblioteket.
Spørsmål?
Har vi gått over binær annet enn at 5 sekunder i første forelesning?
Jeg tror ikke det.
Du bør vite alle de store Os at vi har gått over,
og du bør være i stand til, hvis vi ga deg en funksjon,
bør du være i stand til å si at det er stor O, omtrent. Eller godt, er stor O grov.
Så hvis du ser nestet for løkker looping over samme rekke ting,
som int i, i <n; int j, j > [student] n squared. >> Det tendens til å være n squared.
Hvis du har trippel nestet, pleier det å være n cubed.
Så den slags ting du bør være i stand til å peke ut umiddelbart.
Du trenger å vite innsetting sortere og boble sortere og flette sortere og alle disse.
Det er lettere å forstå hvorfor de er de n kvadrerte og n log n og alle som
fordi jeg tror det var på en quiz ett år der vi i utgangspunktet ga deg
en implementering av boble sortere og sa: «Hva er kjøretiden til denne funksjonen?"
Så hvis du kjenner det som boble sortere, så kan du umiddelbart si n squared.
Men hvis du bare ser på det, trenger du ikke engang trenger å innse det boble slag;
du kan bare si at dette er å gjøre dette og dette. Dette er n squared.
[Student] Er det noen tøffe eksempler du kan komme opp med,
som en lignende idé å finne ut?
>> Jeg tror ikke vi vil gi deg noen tøffe eksempler.
Boblen slags ting er omtrent like tøff som vi ville gå,
og selv det, så lenge du forstår at du gjentar i rekken
for hvert element i matrisen, som er tenkt å være noe som er n squared.
Det er generelle spørsmål, som akkurat her vi har - Oh.
Bare den andre dagen, Doug hevdet: "Jeg har oppfunnet en algoritme som kan sortere en rekke
"Av n tall i O (log n) tid!"
Så hvordan vet vi at er umulig?
[Uhørlig student respons] >> Ja.
I det minste, må du ta hvert element i matrisen,
så det er umulig å sortere en rekke -
Hvis alt er i usortert orden, så du kommer til å berøre alt i rekken,
så det er umulig å gjøre det på mindre enn O n.
[Student] Du viste oss at eksempel på å være i stand til å gjøre det i O n
hvis du bruker mye minne. >> Ja.
Og that - jeg glemmer hva that - Er det teller slag?
Hmm. Som er et heltall sortering algoritme.
Jeg var på utkikk etter den spesielle navn for dette at jeg ikke kunne huske forrige uke.
Ja. Disse er de typene sorterer som kan utføre ting i store O n.
Men det er begrensninger, som du bare kan bruke heltall opp til et visst antall.
Pluss hvis du prøver å sortere noe that -
Hvis matrise er 012, -12, 151, 4 millioner,
så det eneste element skal fullstendig ødelegge hele sortering.
>> Spørsmål?
[Student] Hvis du har en rekursiv funksjon, og det bare gjør rekursive samtaler
innenfor en retur uttalelse, er at halen rekursive
og så ville det ikke bruke mer minne under kjøring
eller det ville i det minste bruke sammenlignbare minne som en iterativ løsning?
[Bowden] Ja.
Det ville trolig være noe lavere, men egentlig ikke.
Hale rekursiv er ganske bra.
Ser igjen på stack rammer, la oss si vi har hoved
og vi har int bar (int x) eller noe.
Dette er ikke et perfekt rekursiv funksjon, men gå tilbake bar (x - 1).
Så åpenbart, dette er feil. Du trenger basen saker og ting.
Men ideen her er at dette er halen rekursive
som betyr at når main samtaler bar det kommer til å få sin stack ramme.
I denne bunken ramme det kommer til å bli en liten blokk med minne
som korresponderer med dens argument x.
Og så la oss si viktigste skjer ringe bar (100);
Så x kommer til å starte ut som 100.
Dersom kompilatoren erkjenner at dette er en hale rekursiv funksjon,
så når bar gjør sin rekursive kall til bar,
stedet for å lage en ny bunke ramme, som er der stabelen begynner å vokse i stor grad,
til slutt vil det kjøre inn i haugen og så får du segfaults
fordi minnet begynner å kollidere.
>> Så i stedet for å lage sin egen stack ramme, kan det realisere,
hei, jeg egentlig aldri trenger å komme tilbake til denne stabelen ramme,
så i stedet jeg vil bare erstatte dette argumentet med 99 og deretter starte bar over alt.
Og da vil det gjøre det igjen, og det vil nå returnere bar (x - 1),
og i stedet for å lage en ny bunke ramme, vil det bare erstatte sin nåværende argument med 98
og deretter hoppe tilbake til begynnelsen av linjen.
Disse operasjonene, erstatte det en verdi på stakken og hoppe tilbake til begynnelsen,
er ganske effektiv.
Så ikke bare er dette det samme minnebruken som en egen funksjon som er iterativ
fordi du bare bruker en stabel ramme, men du er ikke lider downsides
av å måtte ringe funksjoner.
Samtalefunksjoner kan være litt dyrt fordi det har å gjøre alt dette oppsettet
og teardown og alt dette.
Så dette halen rekursjon er bra.
[Student] Hvorfor det ikke opprette nye trinn?
Fordi det innser det ikke behov for.
Kallet til baren er bare tilbake den rekursive kall.
Slik at det ikke trenger å gjøre noe med returverdi.
Det er bare kommer til å umiddelbart returnere den.
Så det er bare kommer til å erstatte sin egen argumentasjon og starte på nytt.
Og også, hvis du ikke har halen rekursive versjonen,
så får du alle disse barene der når denne baren returnerer
det har å returnere verdien til denne, da bar straks tilbake
og den returnerer verdien til denne, så er det bare kommer til å umiddelbart returnere
og returnere verdien til denne.
Så du sparer dette dukker alle disse tingene ut av stabelen
siden returverdien er bare kommer til å bli vedtatt hele veien tilbake opp uansett.
Så hvorfor ikke bare erstatte vår argument med den oppdaterte argument og starte på nytt?
Hvis funksjonen ikke er halen rekursive, hvis du gjør noe sånt -
[Student] hvis bar (x + 1). >> Ja.
>> Så hvis du setter den i stand, så du gjør noe med returverdi.
Eller selv om du bare gjøre return 2 * bar (x - 1).
Så nå bar (x - 1) må returnere for at det å beregne 2 ganger denne verdien,
så nå det trenger sin egen separate stack frame,
og nå, uansett hvor hardt du prøver, du kommer til å trenge å -
Dette er ikke hale rekursiv.
[Student] Vil jeg prøve å få en rekursjon å satse på en hale rekursjon -
[Bowden] I en ideell verden, men i CS50 du ikke må.
For å få halen rekursjon, generelt, setter du opp en ekstra argument
hvor baren vil ta int x inn y
og y tilsvarer den ultimate ting du ønsker å returnere.
Så da dette kommer du til å være tilbake bar (x - 1), 2 * y.
Så det er bare et høyt nivå hvordan du forvandle ting å være halen rekursiv.
Men den ekstra argument -
Og så til slutt når du når base case, du bare returnere y
fordi du har vært samler hele tiden avkastningen verdien du vil.
Du slags har gjort det iterativt men ved hjelp av rekursive samtaler.
Spørsmål?
[Student] Kanskje om pekeren aritmetikk, som når du bruker strenger. >> Ja.
Peker aritmetikk.
Når du bruker strenger er det lett fordi strengene er røye stjerner,
chars er evig og alltid en enkelt byte,
og så pekeren aritmetiske tilsvarer vanlig aritmetikk når du arbeider med strenger.
La oss bare si char * s = "hallo".
Så vi har en blokk i minnet.
Den trenger 6 byte fordi du alltid må null terminator.
Og røye * s kommer til å peke til begynnelsen av denne tabellen.
Så s påpeker det.
Nå er dette i utgangspunktet hvordan noen matrise fungerer,
uavhengig av om det var en retur etter malloc eller om det er på stakken.
Hvilkensomhelst matrise er i utgangspunktet en peker til starten av tabellen,
og deretter noen utvalg drift, noe indeksering, er bare å gå inn i denne matrisen en viss forskyvning.
>> Så når jeg sier noe sånt som s [3], og dette kommer til å s og teller tre tegn i.
Så s [3], har vi 0, 1, 2, 3, så s [3] er til å referere til denne l.
[Student] Og vi kunne nå den samme verdien ved å gjøre s + 3 og deretter parentes stjerne?
Ja.
Tilsvarer dette * (r + 3);
og det er for evig og alltid tilsvarende uansett hva du gjør.
Du aldri trenger å bruke braketten syntaks.
Du kan alltid bruke * (s + 3) syntaks.
Folk har en tendens til å like braketten syntaks, skjønt.
[Student] Så alle arrays er faktisk bare pekere.
Det er en liten forskjell når jeg sier int x [4]; >> [student] Skaper at minnet?
[Bowden] Det kommer til å skape fire ints på stakken, så 16 bytes totalt.
Det kommer til å skape 16 bytes på stakken.
x er ikke lagret noe sted.
Det er bare et symbol henviser til starten av tingen.
Fordi du erklærte rekke innsiden av denne funksjonen,
hva kompilatoren kommer til å gjøre er å erstatte bare alle forekomster av variabelen x
med der det skjedde å velge å sette disse 16 bytes.
Det kan ikke gjøre det med char * s fordi s er en faktisk peker.
Det er gratis å peke til andre ting.
x er en konstant. Du kan ikke ha det punktet til en annen tabell. >> [Student] Okay.
Men denne ideen, denne indeksering, er den samme uavhengig av om det er en tradisjonell rekke
eller om det er en peker til noe, eller hvis det er en peker til en malloced array.
Og faktisk, det er så tilsvarer det som også er det samme.
Det faktisk bare oversetter hva er innsiden av vinkelen og hva som er igjen av brakettene,
legger dem sammen, og dereferences.
Så dette er like gyldig som * (s + 3) eller s [3].
[Student] Kan du ha pekere peker til 2-dimensjonale arrays?
>> Det er vanskeligere. Tradisjonelt, nei.
En 2-dimensjonal array er bare en 1-dimensjonal array med noen lettvint syntaks
fordi når jeg sier int x [3] [3], dette er egentlig bare en rekke med ni verdier.
Og så når jeg indeksen, vet kompilatoren hva jeg mener.
Hvis jeg sier x [1] [2], det vet jeg ønsker å gå til andre rad, så det kommer til å hoppe over de første 3,
og da er det ønsker andre ting i det, så det kommer til å få denne.
Men det er fortsatt kun en enkelt-dimensjonal array.
Og så hvis jeg ønsket å tildele en peker til denne matrisen,
Jeg vil si int * p = x;
Typen av x er bare -
Det er grovt å si type x siden det er bare et symbol, og det er ikke en faktisk variabel,
men det er bare en int *.
x er bare en peker til starten av dette. >> [Student] Okay.
Og så vil jeg ikke være i stand til å få tilgang til [1] [2].
Jeg tror det er spesiell syntaks for å erklære en peker,
noe latterlig som int (* p [-. noe helt latterlig jeg ikke engang kjenner.
Men det er en syntaks for å erklære pekere som med parenteser og ting.
Det kan ikke engang la deg gjøre det.
Jeg kunne se tilbake på noe som ville fortelle meg sannheten.
Jeg vil se etter det senere, hvis det er en syntaks for punkt. Men du vil aldri se det.
Og selv syntaksen er så arkaisk at hvis du bruker det, vil folk bli forvirret.
Flerdimensjonale arrays er ganske sjelden som den er.
Du ganske mye -
Vel, hvis du gjør matrise ting det ikke kommer til å være sjeldne,
men i C du sjelden kommer til å bruke flerdimensjonale arrays.
Ja. >> [Student] La oss si du har en veldig lang rekke.
>> Så i virtuelt minne ville det synes å være alt sammenhengende,
som elementene rett ved siden av hverandre,
men i det fysiske minnet, vil det være mulig for at det skal deles opp? >> Ja.
Hvordan virtuelle minnet fungerer er det som skiller nettopp -
Enheten for tildeling er en side, som har en tendens til å være 4 kilobyte,
og så når en prosess sier hei, jeg ønsker å bruke dette minnet,
operativsystemet kommer til å fordele det 4 kilobyte for den lille blokk med minne.
Selv om du bare bruker én liten byte i hele blokken med minne,
operativsystemet kommer til å gi det hele 4 kilobyte.
Så hva dette betyr er at jeg kunne ha - la oss si dette er min stack.
Denne stabel kan skilles. Stabelen min kan være megabyte og megabyte.
Stabelen min kan være stor.
Men bunken selv må deles inn i individuelle sider,
som hvis vi ser på over her la oss si dette er vår RAM,
hvis jeg har 2 GB RAM, dette er faktisk adresse 0 som zeroth byte RAM min,
og dette er 2 gigabyte hele veien ned her.
Så denne siden kan tilsvare denne blokken over her.
Denne siden kan tilsvare denne blokken over her.
Dette kan tilsvare dette over her.
Slik at operativsystemet er gratis å tildele fysisk minne
til en enkelt side vilkårlig.
Og det betyr at hvis denne grensen skjer med skreve en matrise,
en rekke skjer for å bli forlatt av dette og høyre for denne rekkefølgen på en side,
så denne matrisen kommer til å bli delt i fysisk minne.
Og så når du avslutter programmet, når prosessen er ferdig,
disse kartlegginger får slettet og da er det gratis å bruke disse små blokker for andre ting.
Flere spørsmål?
[Student] Pekeren aritmetikk. >> Oh yeah.
Strenger var lettere, men ser på noe som ints,
så tilbake til int x [4];
Hvorvidt dette er en matrise eller om det er en peker til en malloced utvalg av 4 heltall,
det kommer til å bli behandlet på samme måte.
[Student] Så arrays er på haugen?
[Bowden] Arrays er ikke på haugen. >> [Student] Oh.
>> [Bowden] Denne typen matrise tendens til å være på stabelen
med mindre du erklært det på - ignorerer globale variabler. Ikke bruke globale variabler.
Innsiden av en funksjon jeg si int x [4];
Det kommer til å lage et 4-tall blokk på stakken for denne matrisen.
Men dette malloc (4 * sizeof (int)); kommer til å gå på haugen.
Men etter dette punktet kan jeg bruke x og p i ganske mye de samme måter,
annet enn unntakene jeg sa før om du kan tilordne p.
Teknisk, deres størrelser er litt forskjellig, men det er helt irrelevant.
Du aldri bruke sine størrelser.
P jeg kunne si p [3] = 2, eller x [3] = 2;
Du kan bruke dem i nøyaktig de samme måter.
Så peker aritmetikk nå - Ja.
[Student] Har du ikke trenger å gjøre p * hvis du har brakettene?
Konsollene er en implisitt dereferanse. >> Ok.
Egentlig også hva du sier med kan du få flerdimensjonale arrays
med pekere, hva du kan gjøre er noe sånt som, la oss si, int ** pp = malloc (sizeof (int *) * 5);
Jeg skal bare skrive det ut først.
Jeg ønsker ikke at ett.
Okay.
Det jeg gjorde her er - Det burde være pp [i].
Så pp er en peker til en peker.
Du mallocing pp å peke på en rekke av 5 int stjerner.
Så i minnet du har på stakken pp.
Det kommer til å peke på en rekke av 5 blokker som alle selv pekere.
Og så når jeg malloc her nede, malloc jeg at hver av de individuelle pekere
skal peke til en egen blokk av 4 byte på haugen.
Så dette peker på fire byte.
Og dette peker til en annen 4 byte.
>> Og alle av dem peker på sine egne fire byte.
Dette gir meg en måte å gjøre flerdimensjonale ting.
Jeg kan si pp [3] [4], men nå er dette ikke det samme som flerdimensjonale arrays
fordi flerdimensjonale matriser den oversatt [3] [4] i en enkelt offset i X-matrisen.
Dette dereferences p, åpner den tredje indeksen, da dereferences som
og tilganger - 4 ville være ugyldig - den andre indeksen.
Mens når vi hadde int x [3] [4] før som en flerdimensjonal matrise
og når du dobbeltklikker braketten det er egentlig bare en enkelt dereferanse,
du etter en enkelt pekeren og deretter en offset,
dette er virkelig 2D referanser.
Du følger to separate pekere.
Så dette også teknisk lar deg ha flerdimensjonale arrays
hvor hver enkelt matrise er forskjellige størrelser.
Så jeg tror taggete flerdimensjonale arrays er hva det heter
siden egentlig det første kunne vise til noe som har 10 elementer,
den andre tingen kunne vise til noe som har 100 elementer.
[Student] Er det noen grense for hvor mange tips du kan ha
peker til andre pekere? >> Nei
Du kan ha int ***** p.
Tilbake til pekeren aritmetiske - >> [student] Oh. >> Ja.
[Student] Hvis jeg har int *** p og så gjør jeg en dereferencing og jeg sier p * er lik denne verdien,
er det bare kommer til å gjøre en grad av dereferencing? >> Ja.
Så hvis jeg ønsker å få tilgang til ting som det siste pekeren peker på -
Så gjør du *** p. >> Ok.
Så dette er p peker på en blokk, peker på en annen blokk, peker til en annen blokk.
Så hvis du gjør * p = noe annet, så du endrer dette
til nå peker til en annen blokk. >> Ok.
>> [Bowden] Og hvis disse ble malloced, da har du nå lekket minne
med mindre du tilfeldigvis har forskjellige referanser av disse
siden du ikke kan komme tilbake til de de som du bare kastet bort.
Peker aritmetikk.
int x [4]; kommer til å tildele en rekke 4 heltall
der x kommer til å peke på begynnelsen av tabellen.
Så når jeg sier noe sånt x [1]; jeg vil den skal bety gå til andre heltall i rekken,
som ville være denne.
Men virkelig, det er 4 byte inn i matrisen siden dette heltall tar opp 4 bytes.
Så en forskyvning av 1 betyr egentlig en forskyvning av en
ganger størrelsen av uansett type matrisen er.
Dette er en matrise av heltall, slik at den vet å gjøre en ganger størrelsen av int når den ønsker å offset.
Den andre syntaksen. Husk at dette er ekvivalent med * (x + 1);
Når jeg sier pekeren + 1, hva som returnerer er adressen som pekeren er lagring
pluss 1 ganger størrelsen av den typen pekeren.
Så hvis x = ox100, så x + 1 = ox104.
Og du kan misbruke dette og si noe sånt som char * c = (char *) x;
og nå c kommer til å være den samme adressen som x.
c kommer til å være lik ox100,
men c + 1 kommer til å være lik ox101
siden pekeren aritmetiske avhenger av hvilken type peker som du legger til.
Så c + 1, det ser ut på c, det er en røye peker, så det kommer til å legge en ganger størrelsen av røye,
som alltid kommer til å være 1, slik at du får 101,
mens hvis jeg gjør x, som er også fremdeles 100, x + 1 kommer til å bli 104.
[Student] Kan du bruke c + + for å avansere pekeren med 1?
Ja, det kan du.
Du kan ikke gjøre det med x fordi x er bare et symbol, det er en konstant, du kan ikke endre x.
>> Men c skjer til å bare være en peker, så c + + er helt gyldig, og det vil øke med 1.
Hvis c var bare en int *, så c + + ville bli 104.
+ + Gjør pekeren aritmetiske like c + 1 ville ha gjort pekeren aritmetikk.
Dette er faktisk hvor mange ting som fusjonerer sort -
I stedet for å lage kopier av ting, kan du i stedet gå -
Som om jeg ønsket å passere denne halvdelen av tabellen - la oss slette noe av dette.
La oss si at jeg ønsket å passere denne siden av tabellen i en funksjon.
Hva ville jeg gå over til den funksjonen?
Hvis jeg passerer x, jeg passerer denne adressen.
Men jeg ønsker å passere denne adressen. Så hva skal jeg passere?
[Student] Pointer + 2?
[Bowden] Så x + 2. Ja.
Det kommer til å være denne adressen.
Du vil også svært ofte ser det som x [2] og deretter adressen til.
Så du må ta adressen på det fordi braketten er en implisitt dereferanse.
x [2] refererer til verdien som er i denne boksen, og så vil adressen til boksen,
så du sier og x [2].
Så det er hvordan noe i fletting slag der du ønsker å passere halve listen til noe
du egentlig bare passere & x [2], og nå så langt som den rekursive kall er opptatt av,
min nye utvalg starter der.
Siste liten spørsmål.
[Student] Hvis vi ikke sette en ampersand eller - hva er det heter? >> Star?
[Student] Star. >> Teknisk dereferanse operatør, men - >> [student] dereferanse.
>> Hvis vi ikke setter en stjerne eller en ampersand, hva skjer hvis jeg bare si y = x og x er en peker?
Hva er den type y? >> [Student] Jeg vil bare si det er pekeren 2.
Så hvis du bare si y = x, nå x og y peker til det samme. >> [Student] Pek på det samme.
Og hvis x er en int peker? >> Det ville klage fordi du ikke kan tildele pekere.
[Student] Okay.
Husk at pekere, selv om vi trekker dem som piler,
egentlig alt de store - int * x - virkelig alle x er lagring er noe som ox100,
som vi tilfeldigvis til å representere så peker til blokken lagret ved 100.
Så når jeg sier int * y = x, jeg bare kopiere ox100 inn y,
som vi bare kommer til å representere som y, også peker til ox100.
Og hvis jeg sier int i = (int) x; så jeg kommer til å lagre hva verdien av ox100 er
innsiden av den, men nå kommer det til å bli tolket som et heltall i stedet for en peker.
Men du trenger kastet ellers vil klage.
[Student] Så mener du å kaste -
Er det kommer til å bli kastet int av x eller støping int av y?
[Bowden] Hva?
[Student] Okay. Etter disse parentes er det skal være en x eller ay det?
>> [Bowden] Enten. x og y er ekvivalente. >> [Student] Okay.
Fordi de er begge pekere. >> Ja.
[Student] Så det ville lagre den heksadesimale 100 i heltall form? >> [Bowden] Yeah.
Men ikke verdien av hva den peker på.
[Bowden] Yeah. >> [Student] Så bare adressen i heltall form. Okay.
[Bowden] Hvis du ønsket å for noen bisarre grunn,
du kan utelukkende håndtere pekere og aldri håndtere heltall
og bare være som int * x = 0.
Så du kommer til å bli veldig forvirret når pekeren aritmetiske begynner å skje.
Så tallene at de lagrer er meningsløse.
Det er bare hvordan du ende opp tolke dem.
Så jeg er fri til å kopiere ox100 fra en int * til en int,
og jeg er fri til å tildele - du sannsynligvis kommer til å få skreket til for ikke å kaste -
Jeg er fri til å tildele noe sånt (int *) ox1234 inn i denne vilkårlig int *.
Så ox123 er like gyldig et minne adresse som er & y.
& Y skjer å returnere noe som er ganske mye ox123.
[Student] Vil det være en veldig kul måte å gå fra heksadesimale til desimal form,
som om du har en peker og du kastet det som en int?
[Bowden] Du kan egentlig bare skrive ut ved hjelp som printf.
La oss si at jeg int y = 100.
Så printf (% d \ n - som du bør allerede vet - ut at som et heltall,% x.
Vi får bare skrive det som heksadesimal.
Så en peker ikke lagres som heksadesimale,
og et heltall ikke er lagret som desimaltegn.
Alt er lagret som binært.
Det er bare det at vi har en tendens til å vise pekere som heksadesimal
fordi vi tenker på ting i disse 4-byte blokker,
og minneadresser tendens til å være kjent.
Vi er som, hvis den starter med bf, så det skjer for å være på stakken.
Så det er bare vår tolkning av pekere som heksadesimal.
Okay. Noen siste spørsmål?
>> Jeg vil være her for litt etter hvis du har noe annet.
Og det er slutten på den.
>> [Student] Yay! [Applaus]
>> [CS50.TV]