[Powered by Google Translate] [Avsnitt 4 - Mer Bekväm]
[Rob Bowden - Harvarduniversitetet]
[Detta är CS50. - CS50.TV]
Vi har en frågesport i morgon, om ni inte visste det.
Det är i princip på allt du kan ha sett i klassen eller borde ha sett i klassen.
Det inkluderar pekare, även om de är en mycket ny tråd.
Du bör åtminstone förstå den höga dem.
Allt som var borta över i klassen bör du förstå att testet.
Så om du har frågor om dem, kan du be dem nu.
Men detta kommer att bli en mycket elev-ledda session där ni ställer frågor,
så förhoppningsvis folk har frågor.
Har någon frågor?
Ja. >> [Elev] Kan du gå över pekare igen?
Jag ska gå över pekare.
Alla dina variabler lever nödvändigtvis i minnet,
men brukar du oroa dig inte om det och du bara säga x + 2 och y + 3
och kompilatorn kommer att räkna ut var de saker lever för dig.
När du arbetar med pekare, nu är du uttryckligen använder dessa minnesadresser.
Så en enda variabel kommer alltid bara bor på en enda adress vid varje given tidpunkt.
Om vi ​​vill förklara en pekare, vad den typ kommer att se ut?
>> Jag vill förklara en pekare sid. Hur ser den typ ut?
[Eleven] int * p. >> Ja. Så int * p.
Och hur gör jag det pekar på x? >> [Elev] Ampersand.
[Bowden] Så ampersand är bokstavligen kallas adressen operatören.
Så när jag säger och X det blir minnesadressen för variabeln x.
Så nu har jag pekaren p, och var som helst i min kod jag kan använda * p
eller jag kunde använda x och det kommer att bli exakt samma sak.
(* P). Vad är detta gör? Vad betyder det stjärna?
[Elev] Det betyder ett värde på den punkten. >> Ja.
Så om vi ser på det, kan det vara mycket värdefullt att dra ut diagrammen
där detta är en liten låda med minne för x, som råkar ha värdet 4,
då har vi en liten låda med minne för p,
och så p pekar på x, så vi drar en pil från p till x.
Så när vi säger * p vi säger gå till rutan som är p.
Star är att följa pilen och sedan göra vad du vill med den rutan där.
Så jag kan säga * p = 7, och som kommer att gå till den ruta som är x och förändring som till 7.
Eller jag kunde säga int z = * P * 2, det är förvirrande eftersom det är stjärna, stjärna.
Det en stjärna är dereferencing p, den andra stjärnan multiplicera med 2.
Märker jag kunde ha lika bra ersatt * p med x.
Du kan använda dem på samma sätt.
Och sedan senare jag kan få p pekar på en helt ny grej.
Jag kan bara säga p = &z;
Så nu p inte längre pekar på X, den pekar på z..
Och varje gång jag gör * p är det samma som att göra z..
Så bra med detta är när vi börjar komma in funktioner.
>> Det är typ av meningslöst att förklara en pekare som pekar på något
och då du bara dereferencing det
när du kunde ha använt den ursprungliga variabeln till att börja med.
Men när du kommer in funktioner - så låt oss säga att vi har någon funktion, int foo,
som tar en pekare och bara gör * p = 6;
Som vi såg tidigare med swap, kan du inte göra en effektiv swap och en separat funktion
genom att bara passera heltal eftersom allt i C alltid förbi värde.
Även när du passerar tips du passerar värde.
Det råkar vara så att dessa värden är minnesadresser.
Så när jag säger foo (p), jag passerar pekaren till funktionen foo
och sedan foo gör * p = 6;
Så inne i den funktionen, är * p fortfarande lika med x,
men jag kan inte använda X insidan av denna funktion eftersom det inte scoped inom denna funktion.
Så * p = 6 är det enda sättet jag kan komma åt en lokal variabel från en annan funktion.
Eller, ja, pekare är det enda sättet jag kan komma åt en lokal variabel från en annan funktion.
[Elev] Låt oss säga att du vill returnera en pekare. Hur exakt gör du det?
[Bowden] Returnera en pekare som i något som int y = 3, retur & Y? >> [Elev] Ja.
[Bowden] Okej. Du ska aldrig göra detta. Det är illa.
Jag tror jag såg i dessa föreläsningsbilder du började se hela denna bild av minne
där upp här har du minnesadress 0
och här nere har du minnesadress 4 spelningar eller 2 till 32.
Så du har lite grejer och vissa saker och då har du din stack
och du har din hög, som du precis börjat lära sig om, att växa upp.
[Elev] Är inte högen ovanför stapeln?
>> Ja. Högen är på topp, är det inte? >> [Elev] Tja, satte han 0 ovanpå.
[Elev] Åh, satte han 0 ovanpå. >> [Elev] Åh, okej.
Friskrivningsklausul: Överallt med CS50 du kommer att se det här sättet. >> [Elev] Okej.
Det är bara det att när du första ser stackar,
som när du tänker på en bunt du tänker att stapla saker ovanpå varandra.
Så vi tenderar att vända detta runt så stapeln växer upp som en bunt normalt skulle
istället för stapeln hänger ned. >> [Elev] inte högar tekniskt växa upp också, men?
Det beror på vad du menar med att växa upp.
Stapeln och heap växa alltid i motsatta riktningar.
En stack alltid växer upp i den meningen att den växer upp
mot högre minnesadresser, och heap växer ned
att det växer mot lägre minnesadresser.
Så toppen är 0 och botten är hög minnesadresser.
De är båda växande, bara i motsatta riktningar.
[Elev] Jag menade bara att eftersom du sa att du sätter stacken på botten
eftersom det verkar mer intuitivt eftersom för stapeln att börja på toppen av en hög,
hög är på toppen av sig själv också, så that - >> Ja.
Du tror också av högen som växer upp och större, men stacken mer.
Så stack är det som vi slags vill visa växa upp.
Men överallt man ser något annat kommer att visa adress 0 upptill
och den högsta minnesadress i botten, så det här är din vanliga syn på minnet.
>> Har du en fråga?
[Elev] Kan du berätta mer om högen?
Ja. Jag kommer till det på en sekund.
Först gå tillbaka till varför återvänder och y är en dålig sak,
på stacken har du ett gäng stack ramar som representerar alla funktioner
som har kallats.
Så ignorera tidigare saker är toppen av din stack kommer alltid att vara den viktigaste funktionen
eftersom det är den första funktionen som är som anropas.
Och sedan när du ringer en annan funktion, stapeln kommer att växa ner.
Så om jag kallar någon funktion, foo, och det blir en egen stack ram,
Det kan ringa någon funktion, bar, det blir en egen stack ram.
Och bar kan vara rekursiv och det kan kalla sig,
och så att det andra samtalet till bar kommer att få sin egen stack ram.
Och så vad som händer i dessa stack ramar är alla lokala variabler
och alla funktionsargument som -
Alla saker som är lokalt scoped till denna funktion gå i dessa stack ramar.
Så det betyder när jag sa något i stil bar är en funktion,
Jag ska bara förklara ett heltal och sedan returnera en pekare till den heltal.
Så var bor y?
[Eleven] y bor i baren. >> [Bowden] Ja.
Någonstans i denna lilla torget i minnet är en littler torg som har Y i den.
När jag återvänder & Y, jag returnerar en pekare till denna lilla block av minne.
Men sedan när en funktion återvänder, får sin stack ram dök upp i stacken.
Och det är därför det kallas stack.
Det är som stapeln datastruktur, om du vet vad det är.
Eller ens som en stapel av brickor är alltid exempel,
viktigaste kommer att gå på botten, då den första funktionen du anropar kommer att gå på toppen av det,
och du kan inte gå tillbaka till huvud tills du kommer tillbaka från alla funktioner som har kallats
som har placerats ovanpå den.
>> [Elev] Så om du gjorde tillbaka & Y, detta värde kan ändras utan föregående meddelande.
Ja, it's - >> [elev] Det kan vara över. >> Ja.
Det är helt - Om du försöker och -
Detta skulle också vara en int * bar eftersom det tillbaka en pekare,
så att dess returtyp är int *.
Om du försöker använda returvärdet av denna funktion är det odefinierat beteende
eftersom det pekare pekar dåligt minne. >> [Elev] Okej.
Så vad händer om till exempel, förklarade man int * y = malloc (sizeof (int))?
Det är bättre. Ja.
[Elev] Vi talade om hur när vi drar saker till vår papperskorgen
de är faktiskt inte raderas, vi bara förlora sina pekare.
Så i det här fallet inte radera vi faktiskt värde eller är det fortfarande kvar i minnet?
För det mesta, det kommer att finnas kvar.
Men låt oss säga att vi råkar kalla någon annan funktion, baz.
Baz kommer att få sin egen stack ram på här.
Det kommer att skriva över allt det här,
och sedan om du försöker senare och använda pekare som du fick tidigare,
det kommer inte att vara samma värde.
Det kommer att ha förändrats bara för att du heter funktionen Baz.
[Elev] Men hade vi inte, skulle vi fortfarande få 3?
[Bowden] Med all sannolikhet skulle du.
Men du kan inte lita på det. C säger bara odefinierat beteende.
>> [Elev] Åh, det gör det. Okej.
Så när du vill returnera en pekare, det är där malloc kommer i användning.
Jag skriver faktiskt bara tillbaka malloc (3 * sizeof (int)).
Vi går över malloc mer i en sekund, men tanken på malloc är alla dina lokala variabler
alltid gå på stacken.
Allt som är malloced går på högen, och det kommer alltid och alltid vara på högen
tills du frigöra uttryckligen det.
Så detta betyder att när du malloc något, det kommer att överleva efter funktionen returnerar.
[Elev] Kommer det att överleva efter det att programmet stannar? >> Nej
Okej, så det kommer att vara där tills programmet är helt klar att köra. >> Ja.
Vi kan gå över uppgifter om vad som händer när programmet stannar.
Du kan behöva påminna mig, men det är ett separat sak helt.
[Elev] Så malloc skapar en pekare? >> Ja.
Malloc - >> [elev] Jag tror malloc betecknar ett block av minne som en pekare kan använda.
[Bowden] Jag vill ha diagrammet igen. >> [Elev] Så denna funktion fungerar, men?
[Eleven] Ja, malloc betecknar ett block av minne som du kan använda,
och sedan returnerar adressen för det första blocket i detta minne.
>> [Bowden] Ja. Så när du malloc, du gripa tag i vissa block av minne
Det är för närvarande i högen.
Om stacken är alltför liten, då högen är bara kommer att växa, och det växer i denna riktning.
Så låt oss säga högen är för liten.
Då är det på väg att växa lite och returnera en pekare till detta block som bara växte.
När du gratis grejer, du gör mer utrymme i högen,
så då en senare ringa till malloc kan återanvända det minne som du tidigare hade befriat.
Det viktiga malloc och gratis är att det ger dig fullständig kontroll
under hela livstiden för dessa minnesblock.
Globala variabler är alltid levande.
Lokala variabler är levande inom deras tillämpningsområde.
Så snart du går förbi en klammerparentes, de lokala variablerna är döda.
Malloced minne lever när du vill att det ska vara vid liv
och sedan frigörs när du säger det att släppas.
De är faktiskt de enda 3 typer av minne, verkligen.
Det finns automatisk minneshantering, vilket är stacken.
Saker händer åt dig automatiskt.
När du säger int x, minne allokeras för int x.
När X går utanför ramen, är minnet återvinns för x.
Sedan finns dynamiskt minneshantering, vilket är vad malloc är,
vilket är när du har kontroll.
Du bestämmer dynamiskt när minnet bör och inte bör fördelas.
Och sedan finns det statiska, vilket innebär bara att det lever för evigt,
vilket är vad globala variabler är.
De är bara alltid i minnet.
>> Frågor?
[Elev] Kan du definiera ett block bara genom att använda klammerparenteser
men inte behöva ha en if-sats eller en while eller nåt sånt?
Du kan definiera ett block som i en funktion, men det har klammerparenteser också.
[Elev] Så du kan inte bara ha som en slumpmässig par klammerparenteserna i koden
som har lokala variabler? >> Ja, det kan du.
Inuti int bar vi kunde ha {int y = 3;}.
Det är tänkt att vara här.
Men som definierar fullständigt omfattningen av int y.
Efter den andra klammerparentes kan y inte användas längre.
Man nästan aldrig göra det, dock.
Komma tillbaka till vad som händer när ett program avslutas,
Det är lite av en missuppfattning / halv lögn som vi ger för att bara göra saker och ting lättare.
Vi berättar att när du allokera minne
du fördela lite bit av RAM-minne för den variabeln.
Men du är inte riktigt direkt röra RAM någonsin i dina program.
Om du tänker på det, hur jag ritade -
Och faktiskt, om du går igenom GDB ser du samma sak.
Oavsett hur många gånger du kör ditt program eller vilket program du kör,
stapeln kommer alltid att starta -
du alltid kommer att se variabler runt adress oxbffff något.
Det är oftast någonstans i regionen.
Men hur kan 2 program möjligen ha pekare till samma minne?
[Elev] Det finns några godtyckliga beteckning där oxbfff är tänkt att vara på RAM
som faktiskt kan vara på olika platser beroende på när funktionen hette.
Ja. Termen är virtuellt minne.
Tanken är att varje enskild process, varenda program som körs på din dator
har sin egen - låt oss anta 32 bitar - helt oberoende adress utrymme.
Detta är adressutrymmet.
Den har sina egna helt oberoende 4 gigabyte att använda.
>> Så om du kör 2 program samtidigt, ser detta program 4 gigabyte till sig själv,
Programmet ser 4 gigabyte till sig själv,
och det är omöjligt för detta program dereference en pekare
och sluta med minne från detta program.
Och vad virtuellt minne är en avbildning av en processer adressutrymme
faktiska saker på RAM.
Så det är upp till ditt operativsystem för att veta att,
hey, när den här killen dereferences pekare oxbfff, som egentligen betyder
att han vill RAM-byte 1000,
medan om det här programmet dereferences oxbfff vill han verkligen RAM byte 10000.
De kan vara godtyckligt långt ifrån varandra.
Detta är även sant för saker i en enda processer adress utrymme.
Så som det ser alla 4 gigabyte till sig själv, men låt oss säga -
[Eleven] Har varje process -
Låt oss säga att du har en dator med endast 4 gigabyte RAM-minne.
Ser varenda process hela 4 gigabyte? >> Ja.
Men de 4 gigabyte är det ser är en lögn.
Det är bara den tycker att det har allt detta minne eftersom den inte känner någon annan process finns.
Det kommer bara använda så mycket minne som den faktiskt behöver.
Operativsystemet kommer inte att ge RAM-minne till denna process
om den inte använder något minne i hela regionen.
Det kommer inte att ge det minne för denna region.
Men tanken är att - Jag försöker att tänka på - jag kan inte tänka en analogi.
Analogier är svåra.
En av de frågor som virtuellt minne eller en av de saker det lösa
är att processerna ska vara helt omedvetna om varandra.
Och så kan du skriva ett program som dereferences bara några pekare,
precis skriva ett program som säger * (ox1234)
och det är dereferencing minnesadress 1234.
>> Men det är upp till operativsystemet för att sedan översätta vad 1234 betyder.
Så om 1234 råkar vara en giltig minnesadress för denna process,
som det är på stacken eller något, så kommer detta att returnera värdet på minnesadress
såvitt processen vet.
Men om 1234 är inte en giltig adress, som det händer att landa
i någon liten bit av minnet här som är bortom stacken och bortom högen
och du har inte riktigt använt det, så är det när du får saker som segfaults
eftersom du rör minne som du inte bör röra.
Detta är också sant -
En 32-bitars system innebär 32 bitar har du 32 bitar för att definiera en minnesadress.
Det är därför pekare är 8 byte eftersom 32 bitar 8 byte - eller 4 byte.
Pekare är 4 byte.
Så när du ser en pekare som oxbfffff, är det -
Inom ett visst program kan du bara bygga ett godtyckligt pekare,
allt från ox0 till ox 8 f's - FFFFFFFF.
[Elev] Sa du inte att de är 4 byte? >> Ja.
[Elev] Sedan varje byte har - >> [Bowden] Hexadecimal.
Hexadecimalt - 5, 6, 7, 8. Så pekare du ska alltid se i hexadecimalt.
Det är bara hur vi klassificerar pekare.
Varje 2 siffrorna i hexadecimal är 1 byte.
Så det kommer att bli 8 hexadecimala siffror för 4 byte.
Så varje enskild pekaren på en 32-bitars system kommer att vara 4 byte,
vilket innebär att i processen kan du bygga godtyckliga 4 byte
och gör en pekare av det,
vilket innebär att så långt det är vet, det kan ta en hel 2 till 32 byte minne.
Även om det egentligen inte har tillgång till det,
även om din dator bara har 512 megabyte, tycker att det har så mycket minne.
Och operativsystemet är smart nog att det bara kommer att fördela vad du faktiskt behöver.
Det är inte bara gå, åh, en ny process: 4 spelningar.
>> Ja. >> [Elev] Vad oxen menar? Varför skriver du det?
Det är bara en symbol för hexadecimala.
När du ser ett nummer börjar med oxe, de successiva saker är hexadecimala.
[Elev] Du var förklara om vad som händer när ett program avslutas. >> Ja.
Vad händer när ett program avslutas är operativsystemet
bara raderar avbildningar som den har för dessa adresser, och det är det.
Operativsystemet kan nu bara ge det minnet till ett annat program att använda.
[Elev] Okej.
Så när du fördela något på heapen eller stacken eller globala variabler eller något,
de alla bara försvinna så fort programmet avslutas
eftersom operativsystemet är nu fri att ge det minnet till någon annan process.
[Elev] Även om det finns förmodligen fortfarande värden skrivna i? >> Ja.
Värdena är sannolikt kvar.
Det är bara att det kommer att bli svårt att komma åt dem.
Det är mycket svårare att komma åt dem än det är att få till en raderad fil
eftersom den borttagna filen typ av sitter där under en lång tid och hårddisken är mycket större.
Så det kommer att skriva över olika delar av minnet
innan det händer att skriva över bit av minnet som filen används för att vara på.
Men huvudminnet, RAM, du igenom mycket snabbare,
så det kommer att mycket snabbt skrivas över.
Frågor om detta eller något annat?
[Eleven] Jag har frågor om ett annat ämne. >> Okej.
Har någon frågor om detta?
>> Okej. Annat ämne. >> [Elev] Okej.
Jag gick igenom några av de praktiska testerna,
och i en av dem det talade om sizeof
och det värde som returneras eller olika variabeltyper. >> Ja.
Och det sägs att både int och långa både retur 4, så de är båda 4 byte lång.
Är det någon skillnad mellan en int och en lång, eller är det samma sak?
Ja, det finns en skillnad.
C-standarden -
Jag kommer förmodligen att röra upp.
C-standarden är precis vad C är den officiella dokumentationen av C.
Detta är vad den säger.
Så C-standard säger bara att en röding kommer alltid och alltid vara 1 byte.
Allt efter att - en kort är alltid bara definieras som större än eller lika med en char.
Detta kan vara strikt större än, men inte positivt.
En int är precis definieras som större än eller lika med en kort.
Och en lång är bara definieras som större än eller lika med en int.
Och en lång lång är större än eller lika med en lång.
Så det enda C-standarden definierar den relativa beställning av allt.
Den faktiska mängden minne som saker tar upp är i allmänhet upp till genomförande,
men det är ganska väl definierade vid denna punkt. >> [Elev] Okej.
Så shorts är nästan alltid kommer att vara 2 byte.
Ints är nästan alltid kommer att vara 4 byte.
Långa longs är nästan alltid kommer att vara 8 byte.
Och längtar, det beror på om du använder en 32-bitars eller en 64-bitars system.
Så länge kommer att motsvara den typ av system.
Om du använder en 32-bitars system som apparaten kommer det att bli 4 byte.
Om du använder en 64-bitars som många nya datorer, det kommer att bli 8 byte.
>> Ints är nästan alltid 4 byte på denna punkt.
Långa longs är nästan alltid 8 byte.
Förr i tiden använde ints att endast vara 2 byte.
Men märker att detta helt uppfyller alla dessa relationer är större än och lika med.
Så länge är perfekt får vara samma storlek som ett heltal,
och det är också tillåtet att ha samma storlek som en lång lång.
Och det råkar vara så att det i 99,999% av systemen, kommer det att vara lika med
antingen en int eller en lång lång. Det beror bara på 32-bitars eller 64-bitars. >> [Elev] Okej.
I flyter, hur är decimaltecknet utsetts i termer av bitar?
Liksom som binär? >> Ja.
Du behöver inte känna till att för CS50.
Du behöver inte ens veta att under 61.
Du lär inte som verkligen i någon kurs.
Det är bara en representation.
Jag glömmer den exakta bit kolonilotter.
Idén med flyttal är att man tilldela ett visst antal bitar för att representera -
I grunden är allt i grundpotensform.
Så du tilldela ett visst antal bitar för att representera själva numret, som 1,2345.
Jag kan aldrig representera ett tal med fler siffror än 5.
Sedan kan du tilldela även ett visst antal bitar så att det tenderar att vara som
du kan bara gå upp till ett visst antal, som det är den största exponenten du kan ha,
och du kan bara gå ner till en viss exponent,
gillar det är det minsta exponenten du kan ha.
>> Jag minns inte exakt hur bitarna tilldelas alla dessa värden,
men ett visst antal bitar är dedikerade till 1,2345,
annat visst antal bitar är dedikerade till exponenten,
och det är endast möjligt att representera en exponent av en viss storlek.
[Elev] och en dubbel? Är det som en extra lång flyta? >> Ja.
Det är samma sak som ett flöte än nu du använder 8 byte istället för 4 byte.
Nu kommer du att kunna använda 9 siffror eller 10 siffror,
och detta kommer att kunna gå upp till 300 istället för 100. >> [Elev] Okej.
Och flyter är också 4 byte. >> Ja.
Nåväl, återigen, beror det förmodligen totalt på allmänna genomförandet,
men flottörer är 4 byte, dubbel är 8.
Dubbel kallas dubbel eftersom de är dubbelt så stor flyter.
[Elev] Okej. Och finns det fördubblar dubbelt? >> Det finns inte.
Jag tror - >> [elev] gillar långa Longs? >> Ja. Jag tror inte det. Ja.
[Elev] På förra årets test var det en fråga om huvudfunktionen
behöva vara en del av ditt program.
Svaret var att det inte behöver vara en del av ditt program.
I vilken situation? Det är vad jag såg.
[Bowden] Det verkar - >> [elev] Vad situation?
Har du problemet? >> [Eleven] Ja, jag kan definitivt dra upp den.
Det behöver inte vara tekniskt, men i grunden är det kommer att bli.
[Elev] Jag såg en på en annan år.
Det var som Sant eller falskt: Ett giltigt - >> Åh, en c-fil.?
. [Elev] någon C-filen måste ha - [både talar på en gång - obegripligt]
Okej. Så det är separata.
>> A. C. fil behöver bara innehålla funktioner.
Du kan kompilera en fil i maskinkod, binär, vad som helst,
utan att det är körbar ännu.
En giltig körbar måste ha en huvudfunktion.
Du kan skriva 100 funktioner i 1 fil men ingen viktigaste
och sedan sammanställa det ner till binär,
då du skriver en annan fil som bara har huvud men det kräver en massa av dessa funktioner
I detta binär fil här.
Och så när du gör den körbara, det är vad länken gör
är det kombinerar dessa 2 binära filer till en körbar.
Så en. C. filen inte behöver ha en huvuduppgift alls.
Och på stora kod baser ser du tusentals. C filer och 1 huvudfil.
Fler frågor?
[Elev] Det var en annan fråga.
Det sade att en kompilator. Sant eller falskt?
Och svaret var falsk, och jag förstod varför det är inte som klang.
Men vad kallar vi göra om det inte är?
Göra är i princip bara - jag kan se exakt vad man kallar det.
Men det går bara kommandon.
Gör.
Jag kan dra upp detta. Ja.
Oh, ja. Gör gör också det.
Detta säger syftet med märke verktyget är att fastställa automatiskt
vilka delar av ett stort program behöver byggas
och utfärda kommandon för att kompilera dem.
Du kan göra göra filer som är helt enorm.
Gör tittar på tidsstämplar av filer och som vi sagt tidigare,
Du kan kompilera enskilda filer ned, och det är inte förrän du kommer till länken
att de är tillsammans i en körbar.
Så om du har 10 olika filer och du gör en ändring 1 av dem,
vad märke kommer att göra är bara kompilera att 1 fil
och sedan länka ihop allting.
Men det är mycket dummare än så.
Det är upp till dig att helt definiera att det är vad det borde göra.
Den som standard har förmågan att känna igen det här tidsstämpel,
men du kan skriva en märke fil att göra någonting.
Du kan skriva en make-fil så att när du skriver att det bara cd-skivor till en annan katalog.
Jag började bli frustrerad eftersom jag tack allt inuti mitt Appliance
och då jag visa PDF från Mac.
>> Så jag går till Finder och jag kan gå, Anslut till server,
och servern jag ansluter till är min Appliance, och sedan jag öppnar PDF-
som blir sammanställd av LaTeX.
Men jag var att få frustrerad eftersom varje gång jag behövde uppdatera PDF
Jag var tvungen att kopiera den till en specifik katalog att det kan komma
och det var att få irriterande.
Så istället skrev jag en make-fil som du måste definiera hur det gör saker och ting.
Hur du gör detta är PDF LaTeX.
Precis som alla andra fabrikat fil - eller jag antar att du inte har sett göra filer,
men vi har i apparaten en global fabrikat fil som bara säger,
Om du kompilerar en C-fil, använd klang.
Och så här i min make fil som jag gör säger jag,
den här filen som du kommer att vilja kompilera med PDF LaTeX.
Och så är det PDF-latex som gör det kompilerar.
Gör inte kompilera. Det är bara att köra dessa kommandon i sekvensen jag angett.
Så det går PDF LaTeX, kopierar den till den katalog jag vill att den ska kopieras till,
det CD till katalogen och gör andra saker,
men allt den gör är igen när en fil ändras,
och om det ändrar, så kommer det att köra kommandon som det är tänkt att köra
när filen ändras. >> [Elev] Okej.
Jag vet inte var de globala göra filer är för mig att kolla upp det.
Övriga frågor? Allt från tidigare frågesporter? Eventuella pekaren saker?
Det finns subtila saker med pekare som -
Jag tänker inte att kunna hitta en frågesport fråga på det -
men precis som den här sortens saker.
Se till att du förstår att när jag säger int * x * y -
Det är inte precis något här, antar jag.
Men precis * x * y, de är 2 variabler som är på stacken.
När jag säger x = malloc (sizeof (int)), x fortfarande en variabel på stacken,
malloc är några kvarter över i högen, och vi ska ha X pekar på högen.
>> Så något på stacken pekar på högen.
När du malloc någonting, du lagrar oundvikligen det inne i en pekare.
Så att pekaren är på stacken, är det malloced blocket på högen.
Många människor blir förvirrade och säga int * x = malloc, x är på högen.
Nej Vad X pekar är på högen.
X själv är på stacken, såvida du av någon anledning har X vara en global variabel,
i vilket fall det råkar vara i en annan region av minnet.
Så hålla reda dessa box och pil diagram är ganska vanligt att testet.
Eller om det inte är på frågesport 0, kommer det att vara på frågesport 1.
Du bör känna alla dessa stegen i att sammanställa
eftersom du var tvungen att svara på frågor om dem. Ja.
[Elev] Kan vi gå över dessa steg - >> Visst.
Innan steg och sammanställa vi förbehandling,
sammanställa, montering och länkning.
Förbehandling. Vad gör det?
Det är det enklaste steget i - ja, inte som -
det betyder inte att det borde vara självklart, men det är det enklaste steget.
Ni skulle kunna genomföra det själva. Ja.
[Elev] Ta vad du har i ditt omfattar så här och det kopieras och sedan också definierar.
Det ser för saker som # include och # define,
och det bara kopior och pastor vad de betyder egentligen.
Så när du säger # include cs50.h är preprocessorn kopiera och klistra in cs50.h
till den linjen.
När du säger # define X är 4 går preprocessorn genom hela programmet
och ersätter alla förekomster av x med 4.
Så preprocessorn tar en giltig C-fil och matar en giltig C-fil
där saker har kopierats och klistrats.
Så nu sammanställa. Vad gör det?
[Elev] Det går från C till binärt.
>> [Bowden] Det går inte hela vägen till binära.
[Elev] till maskinkod då? >> Det är inte maskinkod.
[Eleven] Montering? >> Församling.
Det går att församlingen innan den går hela vägen till C-kod,
och de flesta språken göra något liknande.
Välj en högnivåspråk, och om du ska kompilera,
det är sannolikt att kompilera i steg.
Först det kommer att kompilera Python till C, då det kommer att sammanställa C församling,
och därefter församlingen kommer att få översatt till binär.
Så sammanställa kommer att få det från C till församlingen.
Ordet sammanställa betyder oftast att föra det från en högre nivå
till en lägre nivå programmeringsspråk.
Så detta är den enda steg i sammanställningen där du börjar med en högnivåspråk
och hamna i en låg nivå språk, och det är därför steget kallas kompilering.
[Elev] Under sammanställa, låt oss säga att du har gjort # include cs50.h.
Kommer kompilatorn kompilera om cs50.h, liksom de funktioner som finns i det,
och översätta det till assemblerkod också,
eller kommer det att kopiera och klistra in något som har varit förmontering?
cs50.h kommer ganska mycket aldrig hamna i församlingen.
Saker som funktion prototyper och saker är bara för dig att vara försiktig.
Den garanterar att kompilatorn kan kontrollera saker som du ringer funktioner
med rätt avkastning typer och de rätta argumenten och sånt.
>> Så cs50.h ska förbehandlas i filen, och sedan när det sammanställa
Det är i princip kastas bort efter det ser till att allt anropas korrekt.
Men de funktioner som definieras i CS50 biblioteket, som är skild från cs50.h,
de kommer inte separat sammanställas.
Som faktiskt kommer att komma ner i den länkande steget, så vi kommer till det på en sekund.
Men först, vad montering?
[Eleven] församlingen binärt? >> Ja.
Montering.
Vi kallar det inte kompilera eftersom församling är ganska mycket en ren översättning av binära.
Det finns mycket lite logik i att gå från församlingen binärt.
Det är precis som tittar upp i en tabell, åh, vi har denna instruktion;
som motsvarar binära 01.110.
Och så de filer som montering generellt utgångar är. O-filer.
Och. O-filer är vad vi sade tidigare,
hur en fil inte behöver ha en huvuduppgift.
Alla filer kan sammanställas ner till en. O. fil så länge det är en giltig C-fil.
Det kan kompileras ner till. O..
Nu kopplar vad som faktiskt ger ett gäng. O filer och ger dem till en körbar.
Och så vad länkning gör är att du kan tänka på CS50 biblioteket som en. O. fil.
Det är en redan kompilerad binär fil.
Och så när du bygger din fil, din hej.c, som kallar GetString,
hej.c får sammanställt ner till hello.o,
hello.o är nu i binär.
Den använder GetString, så det måste gå över till cs50.o,
och länken smooshes ihop dem och kopierar GetString i den här filen
och kommer ut med en körbar som har alla funktioner man behöver.
Så cs50.o är egentligen inte en O-fil, men det är nära nog att det inte finns någon grundläggande skillnad.
Så koppla bara ger en massa filer tillsammans
som separat innehåller alla funktioner jag behöver använda
och skapar körbara som faktiskt kommer att köras.
>> Och så det är också vad vi sade innan
där du kan få 1000. c-filer, sammanställa du dem alla till. o-filer,
som sannolikt kommer att ta ett tag, sedan ändrar 1. c. fil.
Du behöver bara kompilera att 1. C. fil och sedan länka allt annat,
länka allting tillsammans igen.
[Elev] När vi länkar vi skriver lcs50?
Ja, så lcs50. Att flagga signaler till länken som du bör länka i biblioteket.
Frågor?
Har vi gått över binär annat än att 5 sekunder i den första föreläsningen?
Jag tror inte det.
Du bör känna alla de stora operativsystem som vi har gått över,
och du bör kunna, om vi gav dig en funktion,
Du ska kunna säga att det är Big O, ungefär. Eller också är Big O grov.
Så om du ser kapslade för slingor loopa över samma antal saker,
Liksom int i, i <n, int j, j > [elev] n kvadrat. >> Det tenderar att vara n kvadrat.
Om du har trippel kapslade, tenderar det att vara n kubik.
Så sånt ska du kunna peka ut omedelbart.
Du behöver veta införande sortera och bubbla sortera och sammanfoga sortera och alla dessa.
Det är lättare att förstå varför de är de n fyrkantiga och n log n och allt det
eftersom jag tror att det var på en frågesport ett år där vi i princip gav dig
en implementering av bubbla sortera och sade: "Vad är körtiden för denna funktion?"
Så om du känner igen det som bubbla sortera, då kan du omedelbart säga N kvadrat.
Men om du bara ser på det, behöver du inte ens behöver inse att det är bubbla Sortera;
Du kan bara säga detta gör detta och detta. Detta är n kvadrat.
[Elev] Finns det några tuffa exempel du kan komma med,
som en liknande idé att räkna ut?
>> Jag tror inte att vi skulle ge dig några tuffa exempel.
Bubblan Sortera sak är ungefär lika tuff som vi skulle gå,
och även att, så länge du förstår att du iteration över arrayen
för varje element i arrayen, vilket kommer att vara något som är n kvadrat.
Det finns allmänna frågor, som just här vi har - Oh.
Häromdagen, Doug hävdade: "Jag har uppfunnit en algoritm som kan sortera en array
"Av n tal i O (log n) tid!"
Så hur vet vi att det är omöjligt?
[Ohörbart elev svar] >> Ja.
Åtminstone måste du röra varje element i arrayen,
så det är omöjligt att sortera en array av -
Om allt är i osorterat ordning, då du kommer att röra allt i arrayen,
så det är omöjligt att göra det på mindre än O n.
[Elev] Du visade oss att exempel att kunna göra det i O n
om du använder mycket minne. >> Ja.
Och that - jag har glömt vad that - Är det räknar slag?
Hmm. Det är ett heltal sortering algoritm.
Jag letade efter det särskilda namnet för att jag inte kunde minnas förra veckan.
Ja. Dessa är de typer av slag som kan utföra saker i Big O n.
Men det finns begränsningar, som du kan bara använda heltal upp till ett visst antal.
Plus om du försöker sortera något that -
Om din array är 012, -12, 151, 4 miljoner,
då den enda element kommer att fullständigt förstöra hela sortering.
>> Frågor?
[Elev] Om du har en rekursiv funktion och det bara gör rekursiva anrop
inom en return, det svansen rekursiv,
och så skulle det inte använda mer minne under körning
eller det skulle åtminstone använda jämförbara minne som en iterativ lösning?
[Bowden] Ja.
Det skulle sannolikt vara något långsammare, men inte riktigt.
Svans rekursiv är ganska bra.
Ser igen på stack ramar, låt oss säga att vi har stora
och vi har int bar (int x) eller något.
Detta är inte en perfekt rekursiv funktion, men retur bar (x - 1).
Så uppenbarligen är detta bristfälliga. Du behöver bas ärenden och sånt.
Men tanken här är att det är svansen rekursiv,
vilket innebär att när stora samtal bar det kommer att få sin stack ram.
I denna stack ram det kommer att vara lite block av minne
som motsvarar dess argument x.
Och så låt oss säga viktigaste råkar ringa bar (100);
Så X kommer att börja som 100.
Om kompilatorn är medveten om att detta är en svans rekursiv funktion,
sedan när baren gör sin rekursivt anrop till bar,
istället för att göra en ny stapel ram, som är där stapeln börjar växa stort,
så småningom kommer att köra in i högen och sedan får du segfaults
eftersom minnet börjar kolliderar.
>> Så istället för att göra en egen stack ram, kan det inser,
hej, jag har aldrig riktigt behöver komma tillbaka till denna stack ram,
så istället ska jag bara ersätta detta argument med 99 och sedan börja bar överallt.
Och så kommer det att göra det igen och det kommer att nå tillbaka bar (x - 1),
och istället för att göra en ny stack ram kommer den att ersätta bara dess nuvarande argument med 98
och sedan hoppa tillbaka till början av bar.
Denna hantering, ersätter den 1 värde på stacken och hoppa tillbaka till början,
är ganska effektiv.
Så inte bara är detta samma minnesanvändningen som en separat funktion som iterativ
eftersom du bara använder 1 stack ram, men du inte lider av nackdelar
att behöva anropa funktioner.
Samtalsfunktioner kan vara något dyrt eftersom det har att göra allt denna inställning
och nedkoppling och allt det här.
Så denna svans rekursion är bra.
[Eleven] Varför inte skapa nya steg?
Eftersom det inser att det inte behöver.
Samtalet till bar är bara returnera rekursiva anropet.
Så det behöver inte göra något med returvärdet.
Det är bara att gå till omedelbart lämna tillbaka den.
Så det är bara att byta ut sin egen argumentation och börja om.
Och även om du inte har svans rekursiva versionen,
då får du alla dessa barer där när denna bar returnerar
den måste returnera dess värde till den här, då bar omedelbart tillbaka
och den återgår dess värde till den här, då är det bara att omedelbart återlämna
och returnera dess värde till den här.
Så du sparar detta poppar alla dessa saker av stapeln
eftersom returvärdet är bara kommer att föras hela vägen tillbaka upp ändå.
Så varför inte bara ersätta vårt argument med den uppdaterade argument och börja om?
Om funktionen inte är svans rekursiv, om du gör något liknande -
[Elev] om bar (x + 1). >> Ja.
>> Så om du lägger den i skick, då du gör något med returvärdet.
Eller även om du bara gör retur 2 * bar (x - 1).
Så nu bar (x - 1) måste återvända för att det att beräkna 2 gånger detta värde,
så nu behöver en egen separat stack ram,
och nu, oavsett hur mycket man än försöker, du kommer att behöva -
Detta är inte svans rekursiv.
[Elev] Skulle jag försöka få en rekursion för att sträva efter en svans rekursion -
[Bowden] I en perfekt värld, men i CS50 du behöver inte.
För att få svansen rekursion i allmänhet ställer du in ytterligare ett argument
där bar tar int x till y
och y motsvarar den ultimata sak du vill returnera.
Så då detta du kommer att vara tillbaka bar (x - 1), 2 * y.
Så det är bara en hög nivå hur man förvandlar saker att vara svans rekursiv.
Men den extra argumentet -
Och sedan i slutet när du når din bas fallet återvänder du bara y
eftersom du har ackumulera hela tiden returvärdet som du vill.
Du typ av har gjort det iterativt men med rekursiva anrop.
Frågor?
[Elev] Kanske om pekare aritmetik, som vid användning av strängar. >> Visst.
Pointer aritmetik.
När du använder strängar är det lätt eftersom strängarna är röding stjärnor,
tecken är evigt och alltid en enda byte,
och så visare aritmetik motsvarar vanlig aritmetik när du arbetar med strängar.
Låt oss bara säga char * s = "hej".
Så vi har ett block i minnet.
Det behöver 6 byte eftersom du alltid behöver null avslutare.
Och röding * s kommer att peka på början av denna uppsättning.
Så är pekar där.
Nu är det i princip hur en matris fungerar,
oavsett om det var en avkastning genom malloc eller om det är på stacken.
Varje matris är i grunden en pekare till början av arrayen,
och sedan någon array funktion, varje indexering är bara att gå in i den matris en viss förskjutning.
>> Så när jag säger något i stil med s [3], vilket kommer att s och räkna 3 tecken i.
Så s [3], har vi 0, 1, 2, 3, så s [3] kommer att hänvisa till denna l..
[Elev] Och vi kunde nå samma värde genom att göra s + 3 och sedan parenteser stjärna?
Ja.
Detta är ekvivalent med * (s + 3);
och det är för evigt och alltid lika oavsett vad du gör.
Du behöver aldrig använda fästet syntaxen.
Du kan alltid använda * (s + 3) syntax.
Människor tenderar att vilja fästet syntaxen, dock.
[Elev] Så alla matriser är faktiskt bara pekare.
Det finns en liten skillnad när jag säger int x [4], >> [elev] Betyder det att skapa minnet?
[Bowden] Det kommer att skapa 4 Ints på stacken, så 16 byte totalt.
Det kommer att skapa 16 byte på stacken.
X lagras inte någonstans.
Det är bara en symbol som hänvisar till början av saken.
Eftersom du förklarade matrisen inuti denna funktion,
vad kompilatorn kommer att göra är att byta ut bara alla förekomster av variabeln x
med där det hände att välja att sätta dessa 16 byte.
Det kan inte göra det med char * s eftersom s är en verklig pekare.
Det är gratis att sedan peka på andra saker.
x är en konstant. Du kan inte ha det pekar på en annan array. >> [Elev] Okej.
Men denna idé, denna indexering, är densamma oavsett om det är en traditionell uppsättning
eller om det är en pekare till något eller om det är en pekare till en malloced array.
Och i själva verket är det så likvärdigt att det är också samma sak.
Det faktiskt bara översätter vad som finns inuti av fästena och vad som är kvar av konsolerna,
lägger till dem tillsammans och dereferences.
Så detta är lika giltiga som * (s + 3) eller s [3].
[Elev] Kan du pekare som pekar till 2-dimensionella arrayer?
>> Det är svårare. Traditionellt, nr.
En 2-dimensionell array är bara en 1-dimensionell array med någon lämplig syntax
för när jag säger int x [3] [3], det här är egentligen bara 1 array med 9 värden.
Och så när jag index vet kompilatorn vad jag menar.
Om jag säger x [1] [2], vet det Jag vill gå till den andra raden, så det kommer att hoppa över den första 3,
och sedan vill den andra saken genom att, så det kommer att bli här.
Men det är fortfarande bara en enda endimensionell matris.
Och så om jag ville ge en pekare till den arrayen,
Jag skulle säga int * p = x;
Den typ av x är bara -
Det är grov säger typ av x eftersom det är bara en symbol och det är inte en faktisk variabel,
men det är bara en int *.
x är bara en pekare till början av detta. >> [Elev] Okej.
Och så jag kommer inte att kunna komma åt [1] [2].
Jag tror att det finns speciell syntax för att förklara en pekare,
något löjligt som int (* p [-. något helt löjligt jag vet inte ens.
Men det finns en syntax för att förklara pekare som med parenteser och saker.
Det kanske inte ens låta dig göra det.
Jag kunde se tillbaka på något som skulle berätta sanningen.
Jag kommer att leta efter den senare, om det finns en syntax för punkt. Men du kommer aldrig att se det.
Och även syntaxen är så ålderdomligt att om du använder det, kommer folk att förbryllad.
Flerdimensionella arrayer är ganska sällsynt som det är.
Du ganska mycket -
Tja, om du gör matris saker som den inte kommer att vara sällsynta,
men i C du sällan kommer att använda flerdimensionella arrayer.
Ja. >> [Elev] Låt oss säga att du har en riktigt lång rad.
>> Så i virtuellt minne verkar det vara alla på varandra följande,
Liksom elementen intill varandra,
men i det fysiska minnet, skulle det vara möjligt för den att delas upp? >> Ja.
Hur virtuellt minne fungerar är det skiljer bara -
Enheten för tilldelningen är en sida, som tenderar att vara 4 kilobyte,
och så när en process säger hej, jag vill använda detta minne,
operativsystemet kommer att fördela det 4 kilobyte för den lilla block av minne.
Även om du bara använder en enda liten byte i hela block av minne,
operativsystemet kommer att ge full 4 kilobyte.
Så vad detta betyder är att jag kunde ha - låt oss säga att detta är min stack.
Denna stapel kunde separeras. Min stack kan vara megabyte och megabyte.
Min stack kan vara enorm.
Men stapeln själv måste delas upp i enskilda sidor,
som om vi tittar på här borta låt oss säga att detta är vår RAM,
om jag har 2 gigabyte RAM-minne, är denna faktiska adress 0 som 0:e byte mitt RAM,
och detta är 2 gigabyte hela vägen ner här.
Så här sidan kan motsvara detta block hit.
Denna sida kan motsvara detta block hit.
Denna kan man motsvara detta en hit.
Så operativsystemet är fri att tilldela fysiskt minne
till en enskild sida godtyckligt.
Och det betyder att om denna gräns råkar gränsla en matris,
en matris råkar lämnas om detta och höger om denna ordning en sida,
då matris kommer att delas upp i det fysiska minnet.
Och sedan när du avslutar programmet, när processen avslutas,
Dessa mappningar blir raderas och då är det gratis att använda dessa små block för andra saker.
Fler frågor?
[Elev] Pekaren aritmetik. >> Oh yeah.
Strängar var lättare, men titta på något som Ints,
så tillbaka till int x [4];
Huruvida detta är en matris eller om det är en pekare till en malloced rad 4 heltal,
det kommer att behandlas på samma sätt.
[Elev] Så arrayer är på högen?
[Bowden] Arrayer är inte på högen. >> [Elev] Oh.
>> [Bowden] Denna typ av matris tenderar att vara på stacken
om du förklarade det på - ignorerar globala variabler. Använd inte globala variabler.
Inne i en funktion jag säger int x [4];
Det kommer att skapa en 4-heltal blocket på stacken för denna array.
Men denna malloc (4 * sizeof (int)); kommer att gå på högen.
Men efter detta kan jag använda x och p i stort sett samma sätt,
annat än de undantag jag sa tidigare om du kan tilldela sid.
Tekniskt deras storlek är något annorlunda, men det är helt irrelevant.
Du aldrig använda sina storlekar.
P Jag skulle kunna säga p [3] = 2 eller x [3] = 2;
Du kan använda dem i exakt samma sätt.
Så pekare aritmetik nu - Ja.
[Elev] behöver du inte göra p * om du har konsolerna?
Konsolerna är en implicit dereference. >> Okej.
Egentligen också vad du säger med kan du få flerdimensionella arrayer
med pekare, vad du kan göra är något som, låt oss säga, int ** pp = malloc (sizeof (int *) * 5);
Jag ska bara skriva ut allt först.
Jag ville inte att en.
Okej.
Vad jag gjorde här är - Det borde vara pp [i].
Så pp är en pekare till en pekare.
Du mallocing pp att peka på en rad av 5 int stjärnor.
Så i minnet du har på stacken pp
Det kommer att peka på en rad av 5 block som alla själva pekare.
Och sen när jag malloc här nere, malloc jag att var och en av de enskilda pekare
bör peka på en separat 4 byte på högen.
Så detta pekar på 4 byte.
Och den här pekar till en annan 4 byte.
>> Och alla av dem pekar på sina egna 4 byte.
Det ger mig ett sätt att göra flerdimensionella saker.
Jag skulle kunna säga pp [3] [4], men nu detta är inte samma sak som flerdimensionella arrayer
eftersom flerdimensionella arrayer det översatt [3] [4] till en enda förskjutning i x-array.
Detta dereferences p, åtkomst den tredje indexet, då dereferences som
och tillfarter - 4 skulle vara ogiltigt - det andra indexet.
Medan när vi hade int x [3] [4] innan en flerdimensionell array
och när du dubbelklickar fäste det är egentligen bara en enda dereference,
du följer en enda pekare och sedan en förskjutning,
Detta är verkligen 2D referenser.
Du följer 2 separata pekare.
Så detta också tekniskt kan du ha flerdimensionella arrayer
där varje enskild grupp är olika storlekar.
Så jag tror ojämna flerdimensionella arrayer är vad det kallas
Sedan verkligen det första kan peka på något som har 10 element,
den andra saken skulle kunna peka på något som har 100 element.
[Elev] Finns det någon gräns för hur många tips du kan ha
pekar på andra pekare? >> Nej
Du kan ha int ***** sid.
Tillbaka till pekare aritmetik - >> [elev] Oh. >> Ja.
[Elev] Om jag har int *** p och sedan gör jag en dereferencing och jag säger p * är lika med detta värde,
kommer det bara göra 1 nivå dereferencing? >> Ja.
Så om jag vill komma åt det som det sista pekaren pekar på -
Då du gör *** p. >> Okej.
Så detta är p pekar på 1 block, pekar på ett annat block, pekar på ett annat block.
Sen om du gör * p = något annat, då är du ändra detta
att nu peka på ett annat block. >> Okej.
>> [Bowden] Och om de skulle malloced, då du nu har läckt minne
om du råkar ha olika referenser av dessa
eftersom du inte kan gå tillbaka till de de som du bara kastade bort.
Pointer aritmetik.
int x [4], kommer att fördela en rad 4 heltal
där x kommer att peka på början av arrayen.
Så när jag säger något i stil med x [1], jag vill att det ska betyda gå till den andra heltal i arrayen,
vilket skulle vara här.
Men egentligen, det är 4 byte i arrayen eftersom heltal tar upp 4 bytes.
Så en förskjutning av 1 betyder egentligen en förskjutning av 1
gånger storlek oavsett vilken typ av arrayen är.
Detta är en array av heltal, så det vet att göra 1 gånger storleken på int när den vill att kompensera.
Den andra syntaxen. Kom ihåg att detta är ekvivalent med * (x + 1);
När jag säger pekare + 1, vad att avkastningen är den adress som pekaren lagrar
plus 1 gånger större typen av pekaren.
Så om x = ox100, sedan x + 1 = ox104.
Och du kan missbruka detta och säga något i stil char * c = (char *) x;
och nu c kommer att vara samma adress som x.
c kommer att vara lika med ox100,
men c + 1 kommer att vara lika med ox101
eftersom pekaren aritmetiska beror på vilken typ av pekare som du lägger till.
Så c + 1, det ser på c, det är en röding pekare, så det kommer att lägga 1 gånger storleken på röding,
som alltid kommer att vara 1, så du får 101,
medan om jag gör x, vilket också är fortfarande 100, är ​​x + 1 kommer att vara 104.
[Elev] Kan man använda C + + för att föra pekaren med 1?
Ja, det kan du.
Du kan inte göra det med X eftersom x är bara en symbol, det är en konstant, du kan inte ändra x.
>> Men c råkar bara vara en pekare, så c + + är helt giltiga och det kommer öka med 1.
Om c var bara en int *, då c + + skulle 104.
+ + Gör pekaren aritmetiska precis som C + 1 skulle ha gjort pekare aritmetik.
Detta är faktiskt hur många saker som sammanslagning sort -
Istället för att skapa kopior av saker, kan du skicka istället -
Liksom om jag ville vidarebefordra detta halva arrayen - låt oss ta bort en del av detta.
Låt oss säga att jag ville passera denna sida av matrisen till en funktion.
Vad skulle jag gå till den funktionen?
Om jag passerar X, jag passerar denna adress.
Men jag vill passera denna viss adress. Så vad ska jag gå?
[Studenten] Pointer + 2?
[Bowden] Så x + 2. Ja.
Det kommer att bli den här adressen.
Du kommer också mycket ofta ser det som x [2] och sedan adressen för den.
Så du måste ta adressen av det eftersom fästet är en implicit dereference.
x [2] refererar till det värde som finns i den här rutan, och sedan vill adressen för den rutan,
så du säger och x [2].
Så det är hur något i merge sort där du vill passera halv listan till något
du verkligen bara passerar och x [2], och nu när det gäller den rekursiva anropet är berörda,
min nya array börjar där.
Sista minuten frågor.
[Elev] Om vi ​​inte sätta ett et-tecken eller - vad är det som kallas? >> Star?
[Elev] Star. >> Tekniskt dereference operatör, men - >> [elev] dereference.
>> Om vi ​​inte sätter en stjärna eller ett et, vad händer om jag bara säga y = x och x är en pekare?
Vilken typ av y? >> [Elev] Jag ska bara säga att det är pekare 2.
Så om du bara säga y = x, nu x-och y pekar på samma sak. >> [Elev] Peka på samma sak.
Och om x är en int pekare? >> Det skulle klaga eftersom du inte kan tilldela pekare.
[Elev] Okej.
Kom ihåg att pekare, även om vi drar dem som pilar,
verkligen allt de lagrar - int * x - verkligen alla x lagrar är något som ox100,
som vi råkar representera som pekar på blocket lagras vid 100.
Så när jag säger int * y = x, jag bara kopiera ox100 till y,
som vi bara kommer att representera som Y, även pekar på ox100.
Och om jag säger int i = (int) x; då jag kommer att lagra oavsett värdet på ox100 är
inuti den, men nu det kommer att tolkas som ett heltal i stället för en pekare.
Men du behöver gjutna annars kommer det att klaga.
[Elev] Så menar du att kasta -
Kommer det att vara gjutning int av x eller gjutning int y?
[Bowden] Vad?
[Elev] Okej. Efter dessa parenteser finns det kommer att bli ett x eller ay där?
>> [Bowden] Antingen. x och y är likvärdiga. >> [Elev] Okej.
Eftersom de är båda pekare. >> Ja.
[Elev] Så det skulle lagra hexadecimala 100 i heltal form? >> [Bowden] Ja.
Men inte värdet av vad det pekar på.
[Bowden] Ja. >> [Elev] Så bara adressen i heltal form. Okej.
[Bowden] Om du ville av någon bisarr anledning,
du kan endast hantera pekare och aldrig ta itu med heltal
och bara vara som int * x = 0.
Då du kommer att bli riktigt förvirrad när pekaren aritmetiska börjar hända.
Så de nummer som de lagrar är meningslösa.
Det är bara hur du hamna tolka dem.
Så jag är fri att kopiera ox100 från en int * till en int,
och jag är fri att tilldela - Du är förmodligen kommer att få yelled på för att inte kasta -
Jag är fri att tilldela något liknande (int *) ox1234 i denna godtyckliga int *.
Så ox123 är lika giltiga en minnesadress som är & Y.
& Y råkar återvända något som är ganska mycket ox123.
[Eleven] Skulle det vara en riktigt cool sätt att gå från hexadecimalt till decimalform,
som om du har en pekare och du kastar den som en int?
[Bowden] Du kan egentligen bara ut med som printf.
Låt oss säga att jag har int y = 100.
Så printf (% d \ n - som ni redan borde veta - skriv ut det som ett heltal,% x.
Vi ska bara skriva ut det som hexadecimalt.
Så en pekare inte lagras som hexadecimalt,
och ett heltal inte lagras som decimaltal.
Allt lagras som binära.
Det är bara det att vi tenderar att visa pekare som hexadecimala
eftersom vi tror på saker i dessa 4-byte block,
och minnesadresser tenderar att vara bekant.
Vi är som, om den börjar med bf, då det råkar vara på stacken.
Så det är bara vår tolkning av pekare som hexadecimalt.
Okej. Några sista frågor?
>> Jag kommer att vara här ett tag efter om du har något annat.
Och det är slutet av den.
>> [Elev] Yay! [Applåder]
>> [CS50.TV]