[? 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.