[? DAN ARMADARAS:?] Hej, Jag är [? Dan Armadaras?].
Idag ska vi till att titta på felsökning.
Inte bara kommer vi att prata om vissa tekniker,
men också att vi kommer att titta på några av de funktioner som finns
inom CS50 IDE som tillåter du enkelt felsöka ett program.
>> Bara ett exempel på något som kan gå fel
och det är faktiskt något att vi redan har sett förut.
I det här fallet är det ett C-program som accepterar ett heltal från användaren,
dividerar det med två, och tillhandahåller utsignalen tillbaka till användaren.
Nu från vad vi har sett tidigare i föreläsningar,
Vi vet att detta faktiskt kommer att orsaka specifika typer av division problem
när vi har udda nummer.
>> Specifikt ska vi bara kasta bort något efter decimalkommat.
Nu vet vi att detta råkar vara fallet.
Och om vi kör det, kan vi bekräfta våra misstankar, det första genom att sammanställa.
Och sedan, genom att köra och ange ett udda tal.
>> Detta är inget nytt.
Men detta är faktiskt en exempel på en bugg som
kan existera inom ett större program som blir svårare att spåra.
Även om vi vet vad problemet är den verkliga springande punkten
kanske försöker identifiera särskilt där felet uppstår,
identifiera vad det problemet är, och sedan fastställa det.
Så ge detta som ett exempel av vad som kan vara något
att vi redan vet men kan begravas inom andra delar av koden.
>> Så öppna denna annan källa kodfilen som ett exempel,
denna uppdelning problemet är nu en del av ett större program.
Fortfarande kan vara lite bitars krystat, och vi
kanske kan enkelt identifiera det, särskilt
eftersom vi bara diskutera detta.
Men vi kan räkna ut att detta problem kan existera i en större skala.
>> Om jag sammanställa detta och nu kör det, ange ett udda antal,
Vi kan se att vi inte får exakt utgången att vi kan ha förväntat.
I det aktuella fallet, Vi kan säga att vi
vill räkna alla nummer från en upp till några specifika numret.
Och vi kan se att vi har en rad olika frågor
här om vi utmatning, helt enkelt, 0 och en när vi ger en ingång till 5.
>> Så vi vet redan att det finns ett problem här.
Men vi kanske inte vet exakt där denna fråga faktiskt existerar.
Nu ett av de sätt som vi kan försöka åtgärda detta
är något som vi har redan presenterats för.
Vi kan bara använda den i större skala.
>> På rad 14, har vi denna printf funktion,
som tillåter oss att skriva ut staten av olika bitar av information.
Och detta är något som du bör utnyttja inom ditt program
att försöka ta reda på exakt vad som är händer i olika rader kod.
Så även om detta inte är den slutresultatet att vi faktiskt
vill producera slut detta program, vi fortfarande
kanske har några debug uttalanden där vi
kan försöka lista ut exakt vad händer inne i vår kod.
>> Så i det här fallet, kommer jag printf med felsöknings taggen.
I detta fall, är detta bara en felsöknings sträng
att jag är uppe stötande så att det blir mycket tydligt i produktionen av min kod
vad det är att jag vill visa.
Och utgångs här numret att vi har beräknat.
>> I det här fallet kanske jag vill veta exakt
vad som händer före och efter vissa specifika beräkning.
Så jag kan använda en printf innan och efter det kodrad.
I det här fallet, jag kunde även gör det lite tydligare
genom att säga debug innan och felsöka efter så
att jag inte ihop mig med flera rader som ser identiska.
>> Om vi ​​nu kompilera detta och kör det, ange en siffra som fem igen,
Vi kan se att vi har nu ut innan och efter
och upptäcker att vi inte har gjort en tydlig division eller tydlig med hur många
att vi faktiskt vill göra.
Nu i det här fallet, är detta inte riktigt en tydlig utgång.
Det är egentligen inte en tydlig resultat som vi vill ha ut av detta program.
>> Och detta är, återigen, en lite krystat.
Men, kanske en av de saker som vi kunde göra om specifikationen sa
att vi vill dela detta genom 2 och lägg 1-- så med andra ord,
Vi vill att runda up-- sedan vi skulle veta att vi kunde
gör det viss sak, i detta fall.
Nu här vet vi att vi kommer att vara lägga till en till vår halverats nummer.
>> Låt oss kompilera om detta och bekräfta att
beter sig på det sätt som vi vill.
Vi kan se att nu innan har, har vi nummer 5.
Efter att ha, har vi siffran 3, vilket enligt vår specifikation,
är vad vi ville göra.
Men om vi tittar på utgång här, vi kan
se till att vi kan ha en annan bug helt och hållet, vilket är
att vi börjar vår räkning från 0.
>> Nu återigen, detta är något som vi har sett i det förflutna
och vi kan fixa ganska lätt.
Men i det här fallet, vi hade också förmånen
att använda printf uttalande direkt inne i for-slingan
att veta exakt var att fel förekommer.
Så printf uttalanden är mycket användbart för att hjälpa
du bestämma var, just i din källkod,
ett specifikt fel har inträffat.
>> Och det är också viktigt att inse att när vi skriver kod,
Vi kanske har antaganden om tillståndet i ett program.
Eller vi kanske antaganden om vilken del av programmet
är faktiskt rätt eller fel när senare när vi bygger på det programmet
och göra den till en del av en komplexa och större program
att vi inser att någon aspekt av detta är faktiskt paraplyvagn.
>> Använda printf kan verkligen hjälpa begränsa och identifiera
regionerna i ett program som inte kan beter sig precis så som vi
förvänta, baserat på våra antaganden.
Men det finns andra verktyg tillgängliga, liksom,
som tillåter oss att försöka siffra ut var ett fel inträffar
och även, specifikt, vad saker händer inne i programmet.
>> Så använder printf är mycket användbart när vi vill
att identifiera specifika områden ett program som har vissa programfel.
Men det blir också tråkiga efter ett tag.
I detta fall, är detta en relativt enkelt program
med bara en eller två variabler.
Och det blir mycket lätt för oss att skriva ut värdet av dessa variabler
i samband med det större program.
>> Men vi kan ha en annan program som har många variabler.
Och det kan inte vara helt så lätt att använda printf
för att försöka utvärdera vad som händer till var och en av dessa variabler
eftersom programmet körs.
Det finns ett program som existerar kallas en avlusarprogrammet.
I det här fallet, det som vi kommer användning GNU debugger, eller GDB,
som tillåter oss att inspektera den inre arbetet i ett program i en mycket mer
detaljerat sätt.
>> Vi kan faktiskt köra GDB från kommandoraden
här genom att helt enkelt skriva GDB och kommando som vi vill felsöka.
I det här fallet räknas.
Nu i det här fallet, kan vi se att det leder oss till en prompt som säger GDB.
Och vi kan faktiskt exekvera kommandon till GDB
att faktiskt börja genomförandet av program, stoppa det på vissa punkter,
utvärdera variablerna och inspektera variabler som
finns i programtillståndet vid just det ögonblicket,
och så vidare och så vidare.
Det ger en hel del kraft till oss.
>> Men det råkar vara så att CS50 IDE också
ger en GUI eller en användare gränssnitt för GDB som
tillåter oss att göra detta utan att behöva kommandoradsgränssnitt som helst
eller överhuvudtaget ens.
Det sätt som jag kan komma åt den är att använda debug-knappen
högst upp på CS50 IDE.
Nu i det förflutna, vad vi har sett är att vi använder kommandot
linje att kompilera och sedan köra ett program.
>> Debug knapp gör båda av dessa steg.
Men det kommer också att ta upp debugger fliken längst till höger
som tillåter oss att inspektera en mängd av egenskaper hos programmet
eftersom det körs.
Om jag klickar debug, i detta fall, kommer det att föra upp
en ny flik i konsolen fönstret längst ner.
>> Och du kan se att den här fliken har viss information högst upp.
Och vi kan i stort sett bortse från detta.
Men en av de saker att vi vill märka
är att det avger samma sak som vi
skulle få om vi försökte köra göra på C-programmet i terminalfönstret.
>> Här kan vi se det körs klang, och det har en mängd olika flaggor,
och det sammanställer vår count.c fil, vilket var den valda fliken vid tidpunkten
att jag slog debug.
Så det här är mycket användbar eftersom Nu använder denna debug-knappen,
Vi kan samtidigt sammanställa och sedan exekvera program som vi faktiskt
vill köra.
>> En av de flaggor som är viktigt, i detta fall,
Vi har faktiskt använt under längst tid
men också bara gjorde vissa handen vinka [OHÖRBAR], som
är detta en rätt här.
I klang, säger -ggdb3.
I det här fallet, vad vi är träffande klang, vår kompilator,
är att vi vill kompilera vårt program.
Men också ge vad är kallas symbolinformation
så att kompilatorn faktiskt har tillgång till en hel del av den underliggande informationen
som finns i programmet.
>> Mer specifikt, antalet funktioner som jag har,
namnen på dessa funktioner, variablerna, de typer
att dessa variabler är, och en mängd andra saker som hjälper debugger
utföra sin verksamhet.
Nu finns det något annat det är viktigt att nämna
när vi diskuterar igång ett program på det här sättet.
>> Lägg märke till att det faktiskt har tog upp en ny flik i vår konsol
längs bottnen.
Vi har inte längre att interagera direkt med terminalfönstret.
Men denna nya flik är faktiskt ett terminalfönster.
Det är bara specifika för driften program som vi har skapat.
>> Lägg märke till att vid botten, i kombination med någon utgång
genom klang kompilatorn och GDB, som vi till stor del ignorera,
det faktiskt visar utsignalen från vårt program längst ner.
Nu är det viktigt att inse att ett fönster faktiskt
kommer att visa dig utdata från ditt program
men också kan acceptera input för detta program, liksom.
>> Så meddelande som säger ange en siffra,
vilket är samma utgång som vi hade hade i terminalfönstret innan.
Men det är nu visas i denna ny flik.
Jag kan mata in ett nummer.
Och det kommer faktiskt fungerar som vi förväntar oss
visar oss vår debug, produktion, utgången som kan vara buggig,
som vi har sett förut.
Och längst ned, det faktiskt har några ytterligare utgång
från BNP bara säga att detta program har avslutats.
>> Nu när du såg i detta särskilt genomgång,
det var inte särskilt användbar eftersom även
även om vi hade debugger menyn kommer upp, det var fortfarande ett pågående program.
Vid någon punkt gjorde det faktiskt pausa utförande för oss
för att kunna inspektera alla variablerna packas däri.
Det finns något annat att vi måste göra för att
att få GDB att inse att vi vill ha att pausa genomförandet av programmet
och inte bara låta den fortskrida normalt som vi skulle i alla andra fall.
>> För att pausa utförande, vid någon viss linje,
Vi måste skapa vad som är kallas en brytpunkt.
Och en brytpunkt är mycket lätt skapas i detta CS50 IDE genom att ta musen
och klicka direkt till vänster av vissa specifika linjenummer.
När jag gör det, en röd prick visas, vilket indikerar
att denna linje är nu en brytpunkt.
>> Och nästa gång jag kör GDB, det kommer att stoppa exekvering vid den brytpunkt
när den når den kodrad.
Nu är en viktig sak att inse
att det inte nödvändigtvis så att varje kodrad
är verkligen tillgängliga.
Om jag skulle skapa en funktion här uppe, för example-- void F--
och bara göra en utskrift linje här-- hello world-- om jag aldrig kalla denna funktion,
det kommer att vara så att, om jag ställer en brytpunkt här,
funktionen kommer aldrig att kallas.
Och därför detta särskilt brytpunkt
kommer aldrig paus genomförandet av programmet.
>> Så låt oss säga att jag skapar rätt en brytpunkt på någon kodrad
som faktiskt kommer att utföras.
Nu i det här fallet, är detta första raden i huvudfunktionen.
Så det kommer säkert att bli fallet att så fort jag börjar utförande,
den allra första raden kommer att nås.
GDB pausas exekveringen.
Och då kommer jag att kunna interagera med debugger.
>> Du kan ställa in flera rader som brytpunkter, om du vill.
Vi kan också skapa en linje upp här i detta segment av kod
som aldrig kommer att nås.
Och vi kan också ställa ytterligare nedan.
Anledningen till att vi skulle vill göra detta vi ska
gå in lite mer detalj på bara ett ögonblick.
Så för nu, låt mig bara inaktivera dessa ytterligare brytpunkter
så att vi kan titta på vad som händer när jag har en enda paus
punkt i mitt program.
Jag har gjort några ändringar i detta program.
Så jag måste spara det.
Jag kommer att klicka debug så att jag kan påbörja sammanställning och sedan
genomförandet av debugger.
>> Vi kommer att se att, efter stunder, de linje som vi valt som paus
punkt gulmarkerad.
Vi kan också notera att i övre högra i felsökningspanelen
att pausikonen har vänt i en liten spelikonen.
Det innebär att vi har paus utförande, i det här fallet.
Och slå på knappen Spela skulle tillåter oss att återuppta exekveringen
vid denna punkt.
>> Lägg märke till att det finns ett par andra knappar som i denna debug panel,
också.
Kliva över, vilket gör att jag kan verkställa det en kodrad
och steg över till den linjen till nästa ett, som, i detta fall,
skulle innebära att printf satsen körs.
Och den kommer sedan paus exekvering på ledningen 13, som så.
>> Och det är också ett steg i funktion, som
är bra om jag har skapat andra fungerar på andra håll i källkoden.
Och jag vill kliva in dessa funktioner snarare än
exekvera denna funktion i dess helhet.
Men vi kommer att titta mer på steget i funktion på bara ett ögonblick.
Nu märka några andra saker som faktiskt finns inom denna debug panel.
>> Vi har denna panel som kallas call stack, som visar oss
exakt var vi är.
I det här fallet är vi innanför av huvudfunktionen.
Vårt manus kallas count.c.
Och vi råkar vara på linje 13, kolumn ett, vilket
är exakt vad den markerade regionen av källkoden indikerar, liksom.
>> Nu märker att detta visar också enligt den lokala rörliga delen
alla de variabler som existerar inom denna funktion.
Det är viktigt att notera att alla de variabler
kommer att visas i denna lokal variabel sektion inom en funktion,
redan innan de definieras.
Vi kan se här att vi har en variabel kallas num har ett standardvärde på 0,
och det är av typen int.
>> Nu innan vi faktiskt initiera alla dessa variabler,
vi är inte nödvändigtvis garanterat att se ett värde av 0.
Och beroende på andra avrättningar att du har utfört
och läget i ditt minne när du faktiskt köra programmet,
du kanske upptäcker att du ser inte värdet 0
och, i stället, vissa andra galna siffror.
>> Men oroa dig inte om det.
Det kommer inte att vara relevant tills du faktiskt initiera värdet.
Nu i det här fallet, kan vi se att Jag har utfört några utgångar.
Och jag, just nu, pausade utförande.
Men i detta fall, vad Jag vill verkligen göra
är nu kliva över denna linje kod så att jag kan faktiskt
fråga användaren för att int som Vi vill använda i vårt program.
>> Nu i det här fallet, när Jag slog steg över, meddelande
att Paus eller snarare Resume knappen har ändrats till denna pausknappen
eftersom denna kod faktiskt utför.
Vad händer just nu är att det är
väntar på oss för att mata in viss information som vi kan se från vår produktion text
längst ner.
>> Så just nu är det faktiskt inte stannade,
även om det slags verkar att vara eftersom ingenting händer.
Men det råkar vara så att mitt specifika fall på rad 13,
Jag väntar på indata från användaren.
Och så GDB inte kan inspektera ett program som det körs.
>> Nu nästa gång jag kommer in några input-- så jag kommer in som nummer 5,
som vi har sett i past-- hit Return, och vi
märka det, omedelbart, GDB pauser och, återigen, belyser nästa rad.
Men märker att nu, som en Resultatet av vår inmatning av ett värde,
Vi har uppdaterat detta värde inne av våra lokala variabler, som
är mycket bra att veta exakt vad det numret var i minnet.
>> Nu kan jag låta detta program för att fortsätta spela fram till slutet av dess genomförande
genom att trycka på Fortsätt.
Vi kan se att mycket snabbt gör programmet avsluta verkställande
med samma utgång som vi hade tidigare, debugger stänger,
och nu det här programmet har stannat helt.
>> I visar att bara för syfte att se vad
händer när vi faktiskt drabbats Resume.
Men vi faktiskt kommer att vill gå tillbaka till det här programmet
så att vi kan försöka att felsöka exakt vad som händer.
Nu när jag använder debugger, kan jag inte behöver dessa felsöknings printf uttalanden.
>> Så jag kunde ta bort dem som jag kommer att göra nu bara att gå tillbaka till vår enklare kod
att vi hade för en stund sedan.
Nu när jag sparar programmera och köra den,
Det kommer återigen gå till den första bryta punkt som jag hade på linje 11.
Och jag kommer att kunna inspektera mina variabler som jag vill göra.
>> Det råkar vara så att detta del är inte särskilt intressant,
Och jag vet att jag kommer att skriva ut detta uttalande.
Ange ett nummer.
Och sedan, jag vet att jag kommer att fråga användaren om det heltal.
Så kanske jag faktiskt vill flytta min brytpunkt lite längre ner.
>> Du kan ta bort brytpunkter genom att klicka igen, direkt
till vänster om den linjen numret.
Det röda pricken försvinner, vilket tyder att denna brytpunkt nu är borta.
Nu i det här fallet, utförande har pausats.
Och så det är inte faktiskt kommer att återupptas i detta speciella fall.
Men jag kan ställa in en paus led lite senare.
>> Och när jag nu återuppta min kod, kommer det att återuppta och berätta
poängen med att brytpunkten.
Återigen, jag slog Resume.
Verkar inte som någonting som händer.
Men det beror på min kod väntar på inmatning.
Jag kommer att gå in ett antal 5, tryck Enter, och nu nästa brytpunkt kommer att drabbas.
>> Nu i det här fallet, det här är kodraden
att före, vi visste råkade vara buggy.
Så låt oss utvärdera vad som händer vid denna särskilda tidpunkt.
När en rad är markerad, detta linje ännu inte verkställts.
Så i det här fallet, kan vi se att jag har ett antal, som
Jag har ett heltal kallas num som har ett värde 5.
Och jag kommer att utföra några matematik på det numret.
>> Om jag kliva över det, vi kan Observera att värdet för num
har förändrats i enlighet med det uträkningar som vi faktiskt har gjort.
Och nu när vi är insidan av denna för loop
eller nu när for-slingan själv är markerat
ser vi att vi har en ny variabel som heter i att
kommer att användas i den för slingan.
>> Nu minns innan jag nämnde att du ibland är
kommer att se något slags galen siffror som standard innan det numret
eller att variabeln är faktiskt initieras.
Vi kan se att just här i denna variabel
kallas i, som inte har Ännu initierats
vid tidpunkten för att lyfta fram.
Men vi kan se att det har ett visst antal att vi faktiskt inte skulle förvänta sig.
>> Det är ok.
Oroa dig inte för det eftersom vi har faktiskt inte
initieras det numret tills jag steg över denna linje och värdet
Jag har initierats till värdet 1.
Så för att se att det är faktiskt fallet, låt oss kliva över.
Vi kan nu se att det linje har verkställts.
Och vi nu lyfta fram denna printf linje.
>> Och vi kan nu se hur våra värderingar av i och 3 har förändrats över tiden.
Detta är mycket användbart för att göra, i själva verket är att kliva över linjer flera gånger.
Och du kan hitta vad som egentligen händer inuti din för loop
och vad som händer med variabler inne i den för loop
som programexekvering uppstår ett steg i taget.
>> Nu vid denna tidpunkt, jag klev över bara tillräckligt
att jag nu i slutet av mitt program.
Om jag kliva över det, kommer det faktiskt upphöra exekvering
som vi har sett tidigare.
Låt mig starta om, ännu en gång, så att jag kan peka något annat ut,
också.
>> I detta fall är det Nu frågar mig, igen,
efter ett nummer som Jag kommer igen, anger.
Men den här gången kommer jag att gå in i ett större antal så att för slingan
kommer iterera fler gånger.
I det här fallet kommer jag att ange ett värde av 11.
>> Nu igen eftersom jag skulle ställa en brytpunkt vid linjen 15,
det kommer att belysa den linjen.
Vi kan se att vår nummer 11 är korrekt
representerade i våra lokala variabler.
Kliva över det, kan vi nu titta vad som händer med vårt värde i
som vi fortsätter inne i detta för loop.
Det blir ökas varje gång vi nå toppen av den för loop.
>> Nu en av de saker som kanske vara bra att göra under exekvering
av detta program är för mig att faktiskt ändra variablerna midstream att se
vad som händer med mitt program.
I det här fallet kan jag faktiskt Dubbelklicka på värdet.
Lägg märke till att det blir ett textfält.
>> Nu kan jag gå in olika värde helt och hållet
att se hur mitt program beter när jag har ändrat den variabeln.
Nu i detta fall den variabla I innehåller nu värdet 10.
Men programmet är fortfarande paus i utförandet.
När jag steg över, ser jag att värde i, som jag gick in som 10,
inte är större än värdet av num, som omedelbart orsakar för slingan
att sluta utföra.
>> Nu är inte den enda anledningen till att du skulle
vill ändra variabeln på plats.
Du kan faktiskt vill att försöka ändra den så
att du kan fortsätta exekvering av en slinga
eller så att du kan ändra visst värde innan den
når någon specifik uppsättning av aritmetik att du är på väg att utföra.
>> Så nu när vi faktiskt förändra värde i som programmet kördes,
Det orsakade för slinga för att sluta i förtid på grund, helt plötsligt, i
råkade vara större än värdet av num, vilket innebär att det för loop
inte längre behövs som skall exekveras.
Vidare, hände det att vara så att vi ändrat värdet av i
när linjen 17 betonades, som var den tidpunkt
att för utförande slingan faktiskt utvärderas.
>> Om jag hade ändrat värdet för i på en annan linje, säger 19,
vi skulle ha sett annorlunda beteende eftersom linje 19 skulle
har utfört före slingan villkoret omprövas.
Nu vid denna tidpunkt, jag, återigen, vid slutet av detta program.
Och jag kan låta detta gå till låta mitt program att sluta naturligt.
Men det finns ett par saker som är viktigt att ta bort
från just denna diskussion.
Du måste utvärdera dina egna antaganden
om hur koden ska bete sig.
Varje gång du tror att någon bit kod du vet händer att arbeta,
som kan vara en röd flagga för att gå tillbaka och utvärdera, och vara säker
att ditt antagande av hur den koden fungerar
är faktiskt sant för hur det är uttryckt i källkoden.
>> Men ännu mer till punkt var, när vi använder debugger,
du kan sätta brytpunkter på olika rader kod,
vilket gör att debugger till pausa exekvering vid var och en av dessa linjer
så att du kan utvärdera minne eller ens ändra det på plats.
Och återigen, kom ihåg att du kan skapa flera brytpunkter så att du
kan också återuppta utförande, hoppa över stora delar av kod,
och det kommer automatiskt att pausa vid nästa brytpunkt.
>> Det är faktiskt mer avancerad Utmärkande för debugger, liksom.
Men vi måste hänvisa dig till vissa efterföljande videoklipp
För att verkligen retas isär hur att använda dessa särskilda funktioner.
För nu, tack mycket för att titta på.
Och lycka till felsökning.