[Powered by Google Translate] [SEKCJA 5: mniej wygodne]
[Nate Hardison, Harvard University]
[To jest CS50.] [CS50.TV]
Tak więc witam ponownie, chłopaki.
Witamy w dziale 5.
W tym momencie, po zakończeniu quizu 0 i widząc, jak zrobiłeś,
mam nadzieję, że czujesz się naprawdę dobre, ponieważ byłem bardzo pod wrażeniem wyników w tej sekcji.
Na naszych internetowych widzów, mieliśmy kilka pytań
o ostatnich dwóch problemów na planie problem - czy na quizie, raczej.
Więc mamy zamiar iść na te bardzo szybko tak, że każdy widzi to, co się stało
i jak przejść przez rzeczywistego rozwiązania, a nie tylko oglądania samego rozwiązania.
Będziemy iść w ciągu ostatnich kilku problemów bardzo szybko, 32 i 33.
Wystarczy, ponownie, tak że online widz może zobaczyć.
>> Jeśli zwracasz się do swojego problemu 32, który jest na stronie 13,
13 z 16, problem 32 jest o swapach.
Chodziło o zamianę dwóch liczb całkowitych.
Jest to problem, który wzięliśmy na kilka razy w wykładzie.
A tu, co pytaliśmy ciebie zrobić, to szybkie ślad pamięciowy.
Wypełnić wartości zmiennych są one na stosie
jak kod przechodzi tej funkcji swap.
W szczególności, co patrzymy - Idę umieścić ten iPad dół -
w szczególności, czego szukamy na to jest linia numer 6 tutaj.
I to tylko dla numerach 6 przyległości z poprzednim problemem.
Co chcemy zrobić jest wyświetlane lub oznaczyć stan pamięci
jak to jest w czasie, gdy wykonujemy ten numer linii 6,
który skutecznie powrót z naszej funkcji wymiany tutaj.
Jeśli przewiń tutaj widzieliśmy, że adresy w pamięci wszystko, co zostało przewidziane dla nas.
To jest bardzo klucz; wrócimy do niego za chwilę.
A potem tu na dole, mieliśmy trochę schemat pamięci będziemy się odwoływać.
I rzeczywiście zrobił to na moim iPadzie.
Więc wracam do przełączania się między iPad a kod tylko dla odniesienia.
>> Zacznijmy. Najpierw skupmy się na kilku pierwszych linii głównej tutaj.
Aby rozpocząć, będziemy inicjować x do 1 i Y 2.
Więc mamy dwie zmienne całkowite, oboje będą umieszczone na stosie.
Mamy zamiar umieścić 1 i 2 w nich.
Więc jeśli mogę przerzucić do mojego iPada, miejmy nadzieję, zobaczymy -
Apple TV mirroring i tam iść. Okay.
Więc jeśli mogę przerzucić do mojego iPada,
Chcę zainicjować x do 1 i Y 2.
Robimy to po prostu pisząc 1 w polu oznaczonym x
i 2 w polu oznaczone y. Dość prosty.
Więc teraz wróćmy do laptopa, zobaczyć, co dzieje się dalej.
Więc to następna linia jest gdzie robi się trudne.
Mijamy adres x i adres y jako parametry a i b do funkcji swap.
Adres oraz adres x y są rzeczy, których nie możemy obliczyć
bez odwoływania się do tych punktów kuli aż tutaj.
I na szczęście, pierwsze dwa wypunktowania nam powiedzieć, co dokładnie odpowiedzi są.
Adres w pamięci jest x 10, oraz adres y w pamięci jest 14.
To są wartości, które przejdzie w jak i b się w czołówce naszej funkcji swap.
Więc znowu, przełączenie z powrotem do naszego diagramu, można napisać 10 w
i 14 wb.
Teraz, to jest to, w którym kontynuować wymianę.
Więc skakaniu z powrotem do laptopa ponownie,
widzimy, że sposób wymiany działa jest po raz pierwszy dereference i przechowywać wynik w tmp.
Więc operator dereference mówi: "Hej. Treat zawartości zmiennej jako adres.
Idź do tego, co jest zapisane na ten adres, a następnie załaduj go. "
Co można załadować z zmiennej ma być przechowywane w naszej zmiennej TMP.
Rzut z powrotem do iPad.
Jeśli idziemy do zajęcia 10, wiemy, że adres 10 jest varible x
bo powiedziano nam przez naszego punktu pocisku, że adres w pamięci x jest 10.
Więc możemy tam iść, uzyskać wartość tego, co jest 1, jak widzimy na naszym iPad,
i ładować że w tmp.
Ponownie, nie jest to ostateczna zawartość.
Idziemy na spacer, a my się do naszego ostatecznego stanu programu na końcu.
Ale teraz mamy wartość 1 przechowywane w tmp.
>> I jest szybkie pytanie tutaj.
[Alexander] Czy operator dereference - to jest po prostu prawo gwiazda przed zmienną?
>> Tak. Więc operator dereference, jak przerzucić do naszego laptopa po raz kolejny,
to jest gwiazda tuż.
W tym sensie, że jest - to kontrastować z operatorem mnożenia
które wymaga dwóch rzeczy; operator dereference jest jednoargumentowy operator.
Tylko do jednego zastosowanie, w przeciwieństwie do wartości operatora binarnego
w którym stosuje się dwie różne wartości.
Więc to, co dzieje się w tej linii.
Załadowaliśmy wartość 1 i przechowywano je do naszej zmiennej tymczasowej całkowitą.
Następna linia, możemy przechowywać zawartość b na -
lub raczej, możemy przechowywać zawartość, że B jest wskazujące na miejsce, gdzie jest skierowany do.
Jeśli przeanalizować to od prawej do lewej, idziemy do b. nieprawidłowego,
mamy zamiar zająć 14, mamy zamiar złapać liczbę całkowitą, która jest tam,
a potem będziemy iść na adres 10,
i będziemy rzucać wynik naszej dereference B do tej przestrzeni.
Rzut z powrotem do naszego iPada, gdzie możemy zrobić to trochę bardziej konkretny,
to może pomóc, jeśli piszę numery na wszystkie adresy tutaj.
Wiemy, że na y, jesteśmy w adresie 14, x jest pod adresem 10.
Kiedy zaczynamy w B, mamy dereference b, mamy zamiar pobrać wartość 2.
Mamy zamiar chwycić tę wartość, ponieważ jest to wartość, która mieszka pod adresem 14.
I mamy zamiar umieścić go w zmiennej, która mieszka pod adresem 10,
który jest tam, odpowiadające naszej zmiennej x.
Więc możemy zrobić trochę nadpisania tutaj
gdzie pozbędziemy się naszego 1 i zamiast piszemy 2.
Więc wszystko jest dobrze i dobra w świecie, chociaż mamy nadpisanych x teraz.
Mamy zapisane starą wartość X w naszej zmiennej TMP.
Więc możemy zakończyć swapa z następnego wiersza.
Rzut z powrotem do naszego laptopa.
Teraz pozostaje tylko podjąć zawartość z naszej zmiennej tymczasowej całkowitą
i zapisać je do zmiennej, która mieszka pod adresem, który b trzyma.
Więc będziemy skutecznie b dereference, aby uzyskać dostęp do zmiennej
to, że na adres b posiada w tym,
i jedziemy do rzeczy wartość, tmp jest gospodarstwo do niego.
Rzut z powrotem do iPad raz.
Mogę usunąć tę wartość tutaj, 2,
i zamiast tego będziemy kopiować 1 prawo do niego.
Potem następny wiersz, który wykonuje, oczywiście -
jeśli mamy odwrócić z powrotem do laptopa - to jest punkt 6
który jest punktem, w którym chcieliśmy mieć nasz diagram całkowicie wypełnione.
Więc przerzucanie powrotem do iPada jeszcze raz, tak więc można zobaczyć wypełniony diagram,
widać, że mamy 10 w, 14 w B, 1 w tmp, 2 w X i 1 w Y.
Czy są jakieś pytania na ten temat?
Czy to więcej sensu, po przejściu przez to?
Dodać mniej sensu? Mam nadzieję, że nie. Okay.
>> Wskaźniki są bardzo trudne podlega.
Jeden z facetów, z którymi współpracujemy jest bardzo popularne powiedzenie:
"Aby zrozumieć wskaźniki, należy najpierw zrozumieć wskaźniki".
I myślę, że jest bardzo prawdziwe. Zajmuje to trochę czasu, aby przyzwyczaić się do niego.
Czerpiąc wiele zdjęć, losowanie diagramów pamięci jak ten są bardzo pomocne,
i po przejść przez np. po przykład po przykładzie
będzie to początek, aby trochę więcej rozsądku i trochę więcej rozsądku i trochę więcej sensu.
Wreszcie pewnego dnia, będziesz miał to wszystko całkowicie opanowane.
Wszelkie pytania, zanim przejdziemy do następnego problemu? Dobrze.
Więc przerzucić z powrotem do laptopa.
Kolejnym problemem mamy wiele problemów 33 w pliku I / O.
Powiększyć to trochę mało.
Problem 33 - Tak?
>> [Daniel] miałem tylko szybkie pytanie. Ta gwiazda lub gwiazdka,
to się nazywa dereferencji kiedy użyć gwiazdki przed.
Jak to się nazywa po użyciu ampersand wcześniej?
>> Ampersand przed jest adres-operatora.
Warto więc przejść z powrotem.
Ups. Jestem w trybie powiększenia, tak naprawdę, nie mogę przewijania.
Jeśli spojrzymy na ten kod bardzo szybko tu,
ponownie, to samo się dzieje.
Jeśli przyjrzymy się tym kodem właśnie tutaj, na tej linii, gdzie robimy wywołanie swap,
ampersand jest tylko, że "dostać adres, pod którym mieszka zmiennej x".
Gdy kompilator kompiluje kod,
musi to faktycznie fizycznie zaznaczać miejsca w pamięci dla wszystkich zmiennych, aby żyć.
I co z tego, że kompilator może wtedy zrobić, gdy to skompilowane wszystko
wie, "Oh, I umieścić w adresie 10 x. kładę y pod adresem 14".
Następnie może wypełnić tych wartości dla Ciebie.
Więc możesz - to może następnie przekazać to na przełęczy i & Y w, jak również.
Te Chłopaki uzyskać adres, ale także, kiedy przekazać je do funkcji swap
informacje typu, to int * tu, informuje kompilator,
"Dobra, idziemy do interpretacji tego adresu jako adresu zmiennej całkowitej."
Jako adres int, które różni się od adresu zmiennej znaków
bo int zajmuje, na komputerze 32-bitowym, zajmuje 4 bajty przestrzeni,
natomiast znak zajmuje tylko 1 bajt miejsca.
Tak więc ważne jest, aby wiedzieć, co jest również - co żyje, jaki typ wartości
mieszka pod adresem, który dostał przeszedł w.
Lub adres, który ma się do czynienia.
W ten sposób, wiesz, ile bajtów informacji faktycznie załadować z pamięci RAM.
I wtedy, tak, to operator dereference, jak ty pytali,
idzie i dostęp do informacji w danym adresem.
Więc to mówi, z tym zmiennej tu traktować zawartość jako adres,
przejść do tego adresu, i wyciągnąć, załadować do procesora, obciążenie do rejestru
rzeczywiste wartości lub treści, które żyją pod tym adresem.
Więcej pytań? To są dobre pytania.
To dużo nowej terminologii zbyt.
Jest to także rodzaj funky, widząc & i * w różnych miejscach.
>> Dobrze.
Wracając do problemu 33, plik I / O.
Był to jeden z tych problemów, które myślę, że parę rzeczy się wydarzyło.
Raz, że to dość nowy temat.
Został przedstawiony wkrótce przed quizie,
i myślę, że to było trochę jak jeden z tych problemów, słów w matematyce
gdzie dają ci wiele informacji, ale faktycznie nie kończy się konieczności korzystania mnóstwo niego.
Pierwsza część tego problemu jest opisanie tego, co jest plik CSV.
Teraz, CSV, zgodnie z opisem, jest rozdzielona przecinkami plik wartości.
Powodem są to w ogóle ciekawe, i powód, dla którego nigdy ich używać,
jest, ponieważ, jak wielu z was kiedykolwiek używane rzeczy, jak Excel?
Rysunek większość z was, prawdopodobnie lub będą używać w jakimś momencie swojego życia.
Możesz używać coś jak Excel.
Aby pobrać dane z arkusza kalkulacyjnego Excel czy jakiejkolwiek obróbki z nim,
jeśli chcesz napisać program w C lub program Pythona, program Java,
do czynienia z danymi, które zostały zapisane w tam,
jeden z najbardziej popularnych sposobów, aby uzyskać ją w pliku CSV.
I można otworzyć program Excel i kiedy idziesz do 'Save As' dialogu,
można dostać się rzeczywistą pliku CSV.
>> Handy wiedzieć, jak radzić sobie z tych rzeczy.
Jak to działa jest to, że jest podobny do - mam na myśli, to jest w istocie naśladuje arkusz kalkulacyjny,
gdzie, jak widzimy tutaj, w bardzo skrajny lewy kawałek,
mamy wszystkie nazwiska.
Więc mamy Malan, następnie Hardison, a następnie Bowden, MacWilliam, a następnie Chan.
Wszystkie nazwiska. A potem przecinek oddziela nazwiska od imion.
David, Nate, Rob, Tommy, i Zamyla.
Zawsze mieszać Robby i Toma.
A potem, w końcu, trzecia kolumna to adresy e-mail.
Kiedy zrozumiesz, że reszta z programu jest bardzo prosta do wykonania.
Co mamy zrobić, aby naśladować tę samą strukturę w naszym programie C
jest używaliśmy strukturę.
Zaczniemy grać z nich niewiele więcej, jak również.
Widzieliśmy ich na pierwszy bit w zestawie małego problemu 3, gdy mamy do czynienia ze słowników.
Ale to struct personel przechowuje nazwisko, imię oraz e-mail.
Tak jak nasz plik CSV został przechowywania.
Tak więc jest to po prostu konwersji z jednego formatu na drugi.
Mamy do konwersji, w tym przypadku, struct personelu do linii,
oddzielonych przecinkami line, tak po prostu.
Czy to ma sens? Wy wszyscy wziąć udział w quizie,
więc wyobraź sobie, że przynajmniej miał trochę czasu, aby pomyśleć o tym.
>> W funkcji najmu, problem prosi nas do podjęcia się - we'll powiększyć to trochę mało -
podjąć w strukturze personelu, struct personel, z nazwiskiem,
i dodać jego zawartość do naszego pliku staff.csv.
Okazuje się, że jest to dość proste w użyciu.
Będziemy trochę bawić z tych funkcji nieco więcej dzisiaj.
Ale w tym przypadku, fprintf funkcja jest naprawdę kluczem.
Więc z fprintf, możemy wydrukować, tak jak wy zostały przy użyciu printf cały ten okres.
Możesz printf linii do pliku.
Zamiast więc co zwykłe printf gdzie nadać mu ciąg formatu
a następnie zamienić wszystkie zmienne z następującymi argumentami,
z fprintf, twój pierwszy argument to zamiast plik, który chcesz zapisać.
Gdybyśmy się w tym miejscu do urządzenia, na przykład, człowieka fprintf,
widać różnicę między printf i fprintf.
Będę przybliżyć tutaj trochę.
Więc z printf, dajemy mu ciąg formatu, a następnie kolejne argumenty
są wszystkie zmienne dla wymiany lub zastąpienia w naszym łańcuchu formatu.
Natomiast w fprintf, pierwszy argument jest rzeczywiście ten plik * zwany strumień.
>> Wracając do naszego tu wynajem,
już mamy nasz strumień plik * otworzył dla nas.
To właśnie ten pierwszy wiersz robi, to otwiera plik staff.csv,
otworzy go w trybie dopisywania, i wszystko, co nam pozostało do zrobienia jest
napisz strukturę personelu do pliku.
I zobaczymy, czy chcę korzystać z iPada?
Użyję iPada. Mamy pustkę - Postawmy to na stole, więc można napisać trochę lepiej -
unieważnić wynajem i trwa w jednym argumentem, struktury kadrowej nazwie s.
Dostaliśmy nasze szelki, mamy nasz plik * nazwie pliku,
mamy linię fopen nam dany,
a ja po prostu piszę to jak kropki, ponieważ jest już w Pedia.
I wtedy na naszej następnej linii, mamy zamiar połączyć się fprintf
i zamierzamy przekazać w pliku, który chcemy drukować,
i nasz ciąg formatu, który -
Powiem wam powiedzieć, jak to wygląda.
Jak o tobie, Stella? Czy wiesz, co pierwsza część łańcucha formatu wygląda?
[Stella] Nie jestem pewien. >> Zapraszam do zadawania Jimmy.
Czy wiesz, Jimmy?
[Jimmy] Czy to tylko ostatni? Nie wiem. Nie jestem do końca pewien.
>> Okay. Jak o, czy ktoś dostać to poprawne na egzaminie?
No dobrze.
Okazuje się, że tutaj wszystko, co musimy zrobić, to chcemy każdą część naszej struktury kadrowej
być wydrukowane jako ciąg do naszego pliku.
My po prostu używać znaku podstawiania łańcuchów tekstowych trzy razy, ponieważ mamy nazwisko
następuje przecinek, to imię po nim przecinek,
i wreszcie email który jest następnie - co nie jest
zamontowania na ekranie - ale to następuje znak nowej linii.
Więc mam zamiar napisać to właśnie tam.
I wtedy po naszej ciąg formatu,
musimy tylko podstawienia, które mamy dostęp za pomocą notacji dot
że widzieliśmy w zestawie problemu 3.
Możemy użyć s.last, s.first i s.email
zastąpić w tych trzech wartości do naszego łańcucha formatu.
Więc jak poszło? Ma sens?
Tak? No? Być może? Okay.
>> Ostatnią rzeczą, że robimy po mamy wydrukowane i po my otworzyliśmy nasz plik:
gdy mamy otwarty plik, musimy zawsze pamiętać, aby je zamknąć.
Bo inaczej skończymy wyciek pamięci,
stosując się deskryptory plików.
Tak więc, aby go zamknąć, która funkcja używamy? Daniel?
[Daniel] fclose? >> Fclose, dokładnie.
Więc ostatnia część tego problemu było prawidłowo zamknąć plik, używając funkcji fclose,
który po prostu wygląda.
Nie zbyt szalone.
Cool.
Więc to jest problem, 33 na quiz.
Musimy zdecydowanie więcej plik I / O zbliża.
Zrobimy trochę więcej w wykładzie dzisiaj, lub w sekcji dziś
bo to, co się stanowią większość nadchodzącej PSET.
Przejdźmy od quizu w tym momencie. Tak?
>> [Charlotte]] Dlaczego fclose (plik) zamiast fclose (staff.csv)?
>> Ah. Ponieważ okazuje się, że - tak na pytanie, co jest wielki,
Dlatego też, kiedy piszemy fclose, jesteśmy piśmie fclose (plik), gwiazda zmienna
w przeciwieństwie do nazwy pliku, staff.csv? Czy to prawda? Tak.
Warto więc spojrzeć. Jeśli mam wrócić do mojego laptopa,
i spójrzmy na fclose funkcji.
Więc fclose Zamyka strumień i trwa w wskaźnik do strumienia, który chcemy zamknąć,
w przeciwieństwie do rzeczywistej nazwy pliku, który chcemy zamknąć.
A to dlatego, że za kulisami, kiedy nawiązać połączenie z fopen,
po otwarciu pliku, jesteś rzeczywiście przydzielania pamięci do przechowywania informacji o pliku.
Więc masz wskaźnik pliku, który posiada informacje na temat pliku,
jak to jest otwarte, jego rozmiar, w którym się aktualnie w pliku,
tak, że można dokonać odczytu i zapisu połączeń do danego miejsca w pliku.
Skończyć się zamknięciem wskaźnik zamiast zamykania nazwę pliku.
>> Tak? [Daniel] Aby więc używać wynajem, można by powiedzieć - jak to uzyskać dane wejściowe użytkownika?
Czy fprintf działać jak getString w tym sensie, że będzie to tylko czekać na wejście użytkownika
i poprosić o wpisanie tego - albo czekać na wpisanie tych trzech rzeczy?
Czy trzeba użyć coś wdrożyć wynajem?
>> Tak. Więc nie jesteśmy - pytanie, w jaki sposób możemy uzyskać dane wejściowe użytkownika
w celu realizacji zleceń? I co my tu mamy to rozmówca najmu
uchwalona w tej struktury personelu z wszystkich danych przechowywanych w struktury już.
Więc fprintf jest w stanie po prostu napisać, że dane bezpośrednio do pliku.
Nie ma oczekiwania na polecenia użytkownika.
Użytkownik już podane wejście przez odpowiednio umieszczając go w tej struktury personelu.
I rzeczy, oczywiście, pęknie, jeśli którykolwiek z tych wskaźników były null,
więc przesunąć się z powrotem tu i patrzymy na nasze struktury.
Mamy ciąg ostatni, ciąg 1-ci, email string.
Teraz wiemy, że wszystkie te, tak naprawdę, pod maską, są zmienne char *.
, Które mogą lub nie mogą być skierowane do zera.
Mogą być one w pamięci, wskazując na sterty,
Może pamięci na stosie.
Tak naprawdę nie wiem, ale jeśli któryś z tych wskaźników są nieważne lub nieważne,
że będziemy zdecydowanie awarię naszą funkcję zatrudnić.
To było coś, co było trochę poza zakres egzaminu.
Nie martwisz się o to.
Great. Okay. Więc odejście od quizu.
>> Miejmy zamknąć tego faceta, i będziemy patrzeć na Pset 4.
Więc jeśli macie spojrzeć na specyfikację PSET, raz można go otworzyć, cs50.net/quizzes,
mamy zamiar przejść przez kilka problemów sekcji dziś.
Jestem przewijając - sekcja pytań zaczyna się w trzeciej stronie spec PSET.
I pierwsza część prosi, aby przejść i obejrzeć krótki na przekierowanie i rur.
Który był rodzaj fajne krótkie, pokazuje nowe, fajne sztuczki wiersza polecenia, których można użyć.
A potem mamy kilka pytań do Ciebie.
To pierwsze pytanie o strumieniach, do których pisze printf domyślnie
my niby dotknął tylko trochę chwilą.
Ten fprintf że właśnie omawialiśmy trwa w strumieniu pliku * jako argument.
fclose odbywa się w strumieniu pliku *, jak również,
i wartość zwracana fopen daje strumień pliku *, jak również.
Powodem nie widzieliśmy tych wcześniej, kiedy mamy do czynienia z printf
dlatego printf ma strumień domyślny.
I strumień domyślna, do której pisze
dowiesz się na temat, w skrócie.
Więc na pewno spojrzeć na niego.
>> W dzisiejszej części będziemy mówić trochę o GDB,
ponieważ bardziej zaznajomieni jesteście z nim, więcej praktyki masz z nim,
lepiej będziesz faktycznie wytropić błędy w swoim kodzie.
Przyspiesza to proces debugowania się ogromnie.
Więc za pomocą printf, za każdym razem zrobić, że trzeba przebudować kod
trzeba uruchomić go ponownie, czasem trzeba przesunąć printf wokół,
ustosunkowania się kod, to po prostu trwa.
Naszym celem jest, aby spróbować i przekonać się, że z GDB, można zasadniczo
printf wszystko w dowolnym miejscu w kodzie, a ty nigdy nie musiał przekompilować.
Nigdy nie musisz uruchomić i utrzymać zgadywania gdzie printf następny.
Pierwszą rzeczą do zrobienia jest, aby skopiować ten wiersz i otrzymać kod sekcji wyłączać z sieci.
Jestem kopiowanie tego wiersza kodu, który mówi: "wget ​​http://cdn.cs50.net".
Zamierzam go skopiować.
Mam zamiar iść do mojego urządzenia, pomniejszyć, dzięki czemu można zobaczyć, co robię,
wklejenie go tam, a kiedy nacisnąć enter, to wget polecenie dosłownie to internetowy dostać.
To będzie ciągnąć w dół ten plik z internetu,
i to będzie zapisać go w bieżącym katalogu.
Teraz, jeśli wymienię mój bieżący katalog można zobaczyć, że mam ten plik prawym section5.zip tam.
Sposobem radzenia sobie z tym facetem jest do rozpakowania go,
co można zrobić w linii poleceń, tak jak to.
Section5.zip.
To będzie rozpakuj go, utworzyć folder dla mnie,
nadmuchać całą zawartość, umieścić je w środku.
Więc teraz mogę iść do mojej sekcji 5 katalogu za pomocą komendy cd.
Wyczyść ekran za pomocą jasne. Więc wyczyścić ekran.
Teraz mam ładne czyste terminal do czynienia.
>> Teraz jeśli lista wszystkich plików, które widzę w tym katalogu,
widzisz, że mam cztery pliki: buggy1, buggy2, buggy3 i buggy4.
Ja również, ale ich odpowiednie. Plików C.
Nie będziemy patrzeć na plików. C teraz.
Zamiast tego, mamy zamiar je wykorzystać, kiedy otwieramy GDB.
Musimy na bieżąco je się tak, że mamy dostęp do kodu źródłowego, gdy rzeczywisty używamy GDB,
ale celem tej części rozdziału jest majstrować wokół z GDB
i zobaczyć, w jaki sposób możemy go użyć, aby dowiedzieć się co się dzieje nie tak z każdym z tych czterech programów buggy.
Więc jesteśmy po prostu się po pokoju bardzo szybko,
i mam zamiar poprosić kogoś do uruchomienia jednego z wadliwymi programami
a potem pójdziemy jako grupa przez GDB, a my zobaczymy, co możemy zrobić, aby naprawić te programy,
lub przynajmniej określić, co się dzieje złego w każdym z nich.
Zacznijmy tu z Danielem. Wpadniemy buggy1? Zobaczmy, co się dzieje.
[Daniel] To mówi, że jest błąd aplikacji. >> Tak. Dokładnie.
Więc jeśli mogę uruchomić buggy1, mam SEG usterkę.
W tym momencie, mogę iść i otworzyć buggy1.c,
spróbować dowiedzieć się, co się dzieje nie tak,
ale jeden z najbardziej okropnych rzeczy o tym seg błędu błędu
jest to, że nie mówi nam, co linia z rzeczy program rzeczywiście poszło źle i złamał.
Jesteś rodzaju trzeba patrzeć na kod
i dowiedzieć się, za pomocą przypuszczenia i sprawdzić czy printf, aby zobaczyć, co się dzieje źle.
Jedną z najfajniejszych rzeczy w GDB jest to, że tak naprawdę, naprawdę łatwe
aby dowiedzieć się, na którym twój wiersz awarii programu.
Jest całkowicie warto go używać, nawet jeśli tylko na to.
Więc do rozruchu GDB, wpisuję GDB, a potem dać mu ścieżkę do pliku wykonywalnego, który chcę uruchomić.
Tutaj piszę gdb ./buggy1.
Naciśnij klawisz Enter. Daje mi wszystkie informacje o prawach autorskich,
i tu zobaczysz ten wiersz, który mówi, "czytanie symboli z / home /
jharvard/section5/buggy1 ".
A jeśli wszystko pójdzie dobrze, zobaczysz, to wydrukować wiadomość, że wygląda tak.
Będzie czytać symbole, to będzie powiedzieć "Czytam symboli z pliku wykonywalnego"
a następnie będzie miał ten "Gotowe" wiadomość tutaj.
Jeśli widzisz jakiś inny wariant tego, czy widzisz, że nie mógł znaleźć symbole
lub coś w tym stylu, co to oznacza to, że po prostu nie skompilowaniu wykonywalny prawidłowo.
Kiedy kompilować programy do użytku z GDB, musimy użyć tego specjalnego flagę g,
i że zrobił domyślnie jeśli kompilacji programów, po prostu przez wpisanie się
lub dokonać buggy lub dokonać zwrotu, każdy z nich.
Ale jeśli jesteś kompilacji ręcznie Clang, będziesz musiał iść i to, że-g banderą.
>> W tym momencie, że teraz mamy GDB polecenia,
jest to dość proste do uruchomienia programu.
Możemy wpisać słowo lub możemy po prostu wpisać r.
Większość komend GDB może być skrócona.
Wysyłka do tylko jednej lub kilku liter, które jest bardzo ładne.
Więc Saad, jeśli typ R i naciśnij klawisz Enter, co się dzieje?
[Saad] Mam SIGSEGV, Segmentation fault, a następnie cały ten bełkot.
>> Tak.
Jak widzimy na ekranie, teraz, i jak Saad powiedział,
kiedy wpisać słowo lub R i naciśnij klawisz Enter, ale wciąż ten sam błąd SEG.
Więc za pomocą GDB nie rozwiązuje naszego problemu.
Ale daje nam trochę bełkot, a okazuje się, że to bełkot
rzeczywiście mówi nam, gdzie to się dzieje.
Do obrabiania tego trochę, to pierwszy bit jest funkcja, w której wszystko się dzieje źle.
Jest taki __ strcmp_sse4_2, i mówi nam, że to się dzieje w tym pliku
nazywa sysdeps/i386, wszystko to, ponownie, rodzaj bałaganu - ale linia 254.
To trochę trudne do analizowania. Zazwyczaj, gdy widzisz rzeczy, jak to,
co oznacza, że ​​jest seg uskoki w jednym z bibliotek systemowych.
Więc coś z strcmp. Wy widzieliście strcmp wcześniej.
Nie zbyt szalone, ale to znaczy, że strcmp jest uszkodzony lub, że jest problem z strcmp?
Co sądzisz, Alexander?
[Alexander] Czy to - jest 254 linii? I - nie binarny, ale to nie jest ich sufity,
a potem jeszcze jeden język dla każdej funkcji. Jest to, że w tym funkcji 254, lub -?
>> To linia 254. To wygląda jak w tym pliku. S, więc jest to kod assemblera prawdopodobnie.
>> Ale, myślę, że bardziej palące jest to, ponieważ mamy zdobyć Seg winy,
i wygląda na to, że pochodzi z strcmp funkcji
to oznacza więc, że strcmp jest zepsuty?
Nie powinno, mam nadzieję. Tak tylko dlatego, że masz winy segmentacji
W jednej z funkcji systemu, zwykle oznacza to, że po prostu nie są nazywane prawidłowo.
Najszybszym rzeczą dowiedzieć się, co się właściwie dzieje
gdy widzisz coś szalonego jak to, gdy zobaczysz Seg winy,
zwłaszcza jeśli masz program, który używa więcej niż tylko main,
jest użycie prześledzić.
I skrót backtrace pisząc bt, w przeciwieństwie do pełnej wyrazu backtrace.
Ale Charlotte, co się dzieje, po wpisaniu bt i naciśnij klawisz Enter?
[Charlotte] To pokazuje mi dwie linie, linia 0 i linia 1.
>> Tak. Więc linia 0 i linia 1.
Są to rzeczywiste ramki stosu, które były aktualnie w grze, gdy Twój program rozbił.
Zaczynając od najwyższej ramki, ramki 0, a idąc do dołu najbardziej, czyli ramka 1.
Nasza najwyższa rama jest strcmp frame.
Można myśleć o tym, jako podobny do tego problemu, byliśmy po prostu robi w quizie ze wskaźników,
gdzie mieliśmy zamienić ramkę stosu na górze głównej ramki stosu,
i mieliśmy zmienne, które zamienią się na górze za pomocą zmiennych, że główny został za pomocą.
Oto nasza Crash się w naszym strcmp funkcji, która została wywołana przez naszą główną funkcję,
i backtrace daje nam nie tylko funkcje, w której rzeczy nieudane,
Ale jest to także mówi nam, gdzie wszystko zostało wywołane.
Więc jeśli przewijania nad nieco bardziej w prawo,
widzimy, że tak, że byliśmy na linii 254 tego pliku strcmp-sse4.s.
Ale połączenie zostało dokonane na buggy1.c, linia 6.
To znaczy, że możemy to zrobić - to możemy po prostu iść sprawdzić i zobaczyć, co się dzieje
w buggy1.c, linia 6.
Ponownie, istnieje kilka sposobów, aby to zrobić. Jednym z nich jest, aby wyjść z GDB
lub mieć kod otworzyć w innym oknie i zidentyfikowanie.
To, samo w sobie, jest bardzo przydatny, ponieważ teraz, gdy jesteś w godzinach urzędowania
i masz SEG usterkę a TF zastanawia się, gdzie wszystko było łamanie,
można po prostu powiedzieć: "Och, linia 6. I nie wiem, co się dzieje,
ale coś o linii 6 powoduje mój program do złamania ".
Inny sposób to zrobić to możesz użyć tego polecenia o nazwie lista w GDB.
Możesz również skrócić go l.
Więc jeśli trafiliśmy l, co mamy tutaj?
Mamy całą masę dziwnych rzeczy.
To jest rzeczywisty kod montaż
który jest w strcmp_sse4_2.
Wygląda to rodzaj funky,
a powodem jesteśmy coraz to dlatego teraz,
GDB ma nas w klatce 0.
>> Tak więc w każdej chwili spojrzeć na zmiennych, za każdym razem patrzymy na kod źródłowy,
patrzymy na kodzie źródłowym, który odnosi się do ramki stosu jesteśmy aktualnie znajdujesz
Tak więc w celu uzyskania czegoś sensownego, musimy
przenieść do ramki stosu, że więcej sensu.
W tym przypadku, główna ramka stosu pozwoliłoby trochę więcej sensu,
dlatego, że był w rzeczywistości kod, który napisaliśmy.
Nie strcmp kod.
Sposób można poruszać się między ramkami, w tym przypadku, ponieważ mamy dwa,
mamy 0 i 1,
to zrobić z komend i puchowe.
Jeśli przenieść jedną klatkę,
teraz jestem w głównej ramce stosu.
Można przenieść na dół, aby wrócić do miejsca, gdzie byłem,
Wejdź na górę, przejdź na dół i przejść ponownie.
Jeśli kiedykolwiek zrobić program w GDB, masz awarię, masz prześledzić,
i widać, że jest to w jakimś pliku, że nie wiesz, co się dzieje.
Wypróbuj listę, kod nie wygląda znajomo,
przyjrzeć ramek i dowiedzieć się, gdzie jesteś.
Prawdopodobnie jesteś w niewłaściwym ramce stosu.
Albo przynajmniej, że jesteś w ramce stosu, że nie jest jednym, że naprawdę można debugować.
Teraz jesteśmy w odpowiedniej ramce stosu, jesteśmy w głównym,
Teraz możemy użyć polecenia listy, aby dowiedzieć się, co linia była.
I można go zobaczyć, ale to dla nas drukowane tutaj.
Ale możemy uderzyć listę wszystkich takie same, a lista daje nam ten ładny wydruk
rzeczywistego kodu źródłowego, co dzieje się w tutaj.
>> W szczególności, możemy spojrzeć na linii 6.
Widzimy, co się tutaj dzieje.
I wygląda na to, że robimy porównania łańcuchów
między ciągiem "CS50 skał" i argv [1].
Coś w tym było upaść.
Więc Missy, masz jakieś przemyślenia na temat tego co może być tutaj dzieje?
[Missy] Nie wiem, dlaczego jest upaść. >> Nie wiem, dlaczego to się zawiesza?
Jimmy, jakieś przemyślenia?
[Jimmy] Nie jestem do końca pewien, ale ostatni raz używany ciąg porównania,
lub strcmp, mieliśmy jak trzech różnych sprawach z nim.
Nie masz ==, nie sądzę, prawo w tej pierwszej linii.
Zamiast tego, oddziela się na trzy, a jeden == 0,
był <0, myślę, i jeden był> 0.
Więc może coś w tym stylu? >> Tak. Więc jest to kwestia
z robimy porównania poprawnie?
Stella? Wszelkie myśli?
[Stella] Nie jestem pewien. >> Nie jestem pewien. Daniel? Myśli? Okay.
Okazuje się, co dzieje się tutaj, jest wtedy, gdy prowadziliśmy program
i dostaliśmy Seg winy, po uruchomieniu programu po raz pierwszy, Daniel,
dałeś mu żadnych argumentów wiersza poleceń?
[Daniel] L. >> No W takim razie, co to jest wartość argv [1]?
>> Nie ma wartości. >> Racja.
Cóż, nie ma odpowiedniej wartości string.
Ale jest jakaś wartość. Co to jest wartość, która pobiera przechowywane tam?
>> Wartość śmieci? >> To albo wartość śmieci lub, w tym przypadku,
Koniec tablicy argv jest zawsze zakończone null.
Więc co tak naprawdę dostałem przechowywane jest null.
Inny sposób rozwiązać ten problem, zamiast myśleć to poprzez,
to spróbuj wydrukować go.
To jest, gdy mówię, że przy użyciu GDB jest wielki,
bo można wydrukować wszystkie zmienne, wszystkie wartości, które chcesz
zastosowaniem handy-dandy polecenia P.
Więc jeśli p, a potem wpisać wartość zmiennej lub nazwę zmiennej,
powiedzieć, argc, widzę, że jest 1 argc.
Jeśli chcę wydrukować argv [0], można to zrobić tak po prostu.
I jak widzieliśmy, argv [0] jest zawsze nazwa programu,
zawsze nazwa pliku wykonywalnego.
Tu zobaczysz to ma pełną nazwę ścieżki.
Mogę także wydrukować argv [1] i zobaczyć, co się dzieje.
>> Tutaj mamy taką mistyczną wartością.
Dostaliśmy to 0x0.
Zapamiętaj na początku terminowi rozmawialiśmy szesnastkowej?
Albo że małe pytanie na koniec Pset 0 o tym, jak do reprezentowania 50 w hex?
Sposób zapisu liczb szesnastkowych w CS, tak, aby nie pomylić się
z liczb dziesiętnych, jest zawsze poprzedzić je 0x.
Więc to prefiks 0x zawsze oznacza tylko interpretować następujący numer jako liczba szesnastkowa,
nie jako ciąg znaków, a nie jako liczba dziesiętna, a nie jako liczba binarna.
Ponieważ liczba 5-0 jest ważna liczba w systemie szesnastkowym.
I jest to liczba w postaci dziesiętnej, 50.
Więc to jest po prostu jak dwuznaczności.
Więc 0x0 oznacza szesnastkowy 0, która jest także dziesiętny 0, binarne 0.
To jest po prostu 0 wartość.
Okazuje się, że jest to, co jest null, faktycznie, w pamięci.
Null jest tylko 0.
Tutaj element przechowywany w argv [1] ma wartość null.
Więc staramy się porównać nasz "CS50 Rocks" ciąg na ciąg pusty.
Więc dereferencji null, próbuje uzyskać dostęp do rzeczy na null,
te są zwykle będzie powodować jakieś usterki segmentacji lub innych złych zjawisk.
I okazuje się, że strcmp nie sprawdzić
czy nie został przekazany w wartości, które jest null.
Przeciwnie, tylko idzie do przodu, próbuje zrobić swoje,
i jeśli seg usterek, to seg usterek, i to jest twój problem. Musisz iść go naprawić.
Naprawdę szybko, jak możemy rozwiązać ten problem? Charlotte?
[Charlotte] Można sprawdzić za pomocą if.
Więc jeśli argv [1] ma wartość null, == 0, powrót 1, lub coś [niezrozumiałe].
>> Tak. Jest to więc jeden świetny sposób, aby to zrobić, jak możemy sprawdzić,
wartość zamierzamy przejść do strcmp, argv [1], jest to wartość null?
Jeśli jest null, to możemy powiedzieć, okay, przerwanie.
>> Częściej sposobem na to jest użycie argc wartość.
Można zobaczyć tu na początku main,
pominęli ten pierwszy test, który mamy zazwyczaj robić kiedy używamy argumentów linii poleceń,
które ma sprawdzić czy nasz argc wartość oczekujemy.
W tym przypadku spodziewamy się co najmniej dwa argumenty,
nazwa programu oraz jedna.
Ponieważ mamy zamiar użyć drugiego argumentu tutaj.
Więc o jakieś badania wcześniej, przed naszym strcmp zaproszenia
że testy czy argv jest co najmniej 2, to również zrobić tego samego rodzaju rzeczy.
Możemy sprawdzić, czy to działa, uruchamiając ponownie program.
Zawsze można ponownie uruchomić program w GDB, co jest naprawdę miłe.
Można uruchomić, a kiedy przechodzą w argumenty do programu,
przekazać je w podczas wywołania biegać, nie podczas rozruchu GDB.
W ten sposób można zachować wywoływanie programu z różnych argumentów za każdym razem.
Więc biegnij, lub ponownie, mogę Type R, i zobaczymy, co się stanie, jeśli wpiszesz "hello".
Zawsze będzie pytanie, czy chcesz zacząć ją od początku.
Zazwyczaj, chcę zaczynać je od początku.
I w tym momencie, to uruchamia go ponownie, drukuje się
Program, który kończy nam, buggy1, z argumentem Witam,
i drukuje to standardowe wyjście, to mówi: "Dostajesz D," smutną twarz.
Ale nie seg winy. Stwierdzono, że proces zakończył się normalnie.
Tak, że wygląda całkiem nieźle.
Nie więcej seg winy, my zrobiliśmy to w przeszłości,
więc wygląda na to, że rzeczywiście był seg bug wina, że ​​byliśmy się.
Niestety, to mówi nam, że jesteśmy coraz D.
>> Możemy iść do tyłu i spojrzeć na kod i zobaczyć, co się tam dzieje
dowiedzieć się, co było - dlaczego to mówi nam, że mamy D.
Zobaczmy, tutaj był to printf mówiąc, że masz D.
Jeśli wpisać listę, jak zachować listę pisania, utrzymuje Iterowanie dół programu,
więc to pokazać kilka pierwszych linii programu.
Wtedy to pokazać kolejne kilka linijek, a następny kawałek i następny kawałek.
I będzie ona próbować zejść.
A teraz my się do "linii numer 16 znajduje się poza zasięgiem."
Ponieważ ma tylko 15 wierszy.
Jeśli dojdziesz do tego punktu i Twój zastanawiać: "Co mam zrobić?" możesz użyć polecenia help.
Skorzystać z pomocy, a następnie nadać mu nazwę komendy.
I widzisz GDB daje nam tego rodzaju rzeczy.
Mówi: "Bez argumentów, wymienia dziesięć linie po lub wokół poprzedniej aukcji.
Lista - wymienia dziesięć linii przed - "
Więc spróbuj minus lista.
I że wymienia 10 linie poprzedni, można odtwarzać z listy trochę.
Możesz zrobić listę, liście - można nawet dać listy numer, jak liście 8,
i będzie ona listę 10 linii wokół linii 8.
I można zobaczyć, co dzieje się tu masz proste, jeśli inny.
Jeśli wpiszesz w CS50 skał, drukuje się "Dostajesz A."
W przeciwnym razie wypisuje "Dostajesz D."
Miasto porażka. Dobrze. Tak?
>> [Daniel] Więc kiedy próbowałem robić CS50 skały bez cudzysłowów,
mówi "Dostajesz D."
Potrzebowałem cytaty aby zmusić go do pracy; dlaczego tak jest?
>> Tak. Okazuje się, że kiedy - jest to kolejny fajny smakołyk -
Po uruchomieniu programu, jeśli będziemy go uruchomić i wpisać CS50 skał,
jak Daniel mówił on, i naciśnij Enter,
nadal mówi dostajemy D.
I pytanie, dlaczego tak jest?
I okazuje się, że zarówno nasz terminal i GDB analizować je jako dwa odrębne argumenty.
Bo gdy nie ma miejsca, które jest rozumiane jako
Pierwszy argument zakończony, kolejny argument to się zaczyna.
Sposób na połączenie tych na dwie, lub przepraszam, w jednym argumentem,
jest użycie cudzysłowu.
Więc teraz, jeśli umieścić go w cudzysłowie i uruchomić go ponownie, otrzymamy A.
Więc po prostu zakręcić, bez cudzysłowów, CS50 i skały są analizowane jako dwa odrębne argumenty.
Cytatami, jest analizowany jako jeden argument całkowicie.
>> Widzimy to z przerwania.
Do tej pory został uruchomiony nasz program, a to prowadzi
dopóki nie jest seg usterek lub Hits błąd
lub dopóki nie odszedł i wszystko jest całkowicie w porządku.
To nie jest koniecznie najbardziej przydatne rzeczy, bo czasami
masz błąd w programie, ale to nie jest przyczyną usterki segmentacji.
To nie powodując zatrzymanie programu lub coś podobnego.
Sposób na GDB wstrzymać program w określonym punkcie
jest ustawiony punkt przerwania.
Można to zrobić przez ustawienie przerwania na nazwy funkcji
lub można ustawić punkt przerwania na konkretnej linii kodu.
Chcę ustawić punkty przerwania w nazwach funkcji, bo - łatwe do zapamiętania,
a jeśli rzeczywiście iść i zmienić kod źródłowy się trochę,
wówczas breakpoint rzeczywiście pobyt w tym samym miejscu w kodzie.
Natomiast jeśli używasz numery linii i numery linii zmienić
ponieważ można dodać lub usunąć niektóre kodu, twoje pułapki są zupełnie nie przejmował się.
Jednym z najczęstszych rzeczy zrobić jest ustawiony punkt przerwania na głównej funkcji.
Często będę rozruchu GDB, ja typ B main, naciśnij Enter, i że będzie ustawić punkt przerwania
na głównej funkcji, które po prostu mówi: "wstrzymać program, jak tylko zaczną się"
I w ten sposób, gdy uruchomię mój program, powiedzmy, CS50 skałach dwa argumenty
i naciśnij klawisz Enter, dostaje się do głównych funkcji i zatrzymuje się tuż przy linii pierwszy,
tuż przed ocenia strcmp funkcję.
>> Ponieważ jestem wstrzymane, teraz mogę zacząć mucking wokół i widząc, co się dzieje
z wszystkich różnych zmiennych, które są przekazywane do mojego programu.
Tutaj mogę wydrukować ARGC i zobaczyć, co się dzieje.
Zobacz, że argc jest 3, ponieważ dostał 3 różne wartości w nim.
Ma nazwę programu, nie ma to pierwszy argument, a drugi argument.
Możemy wydrukować te obecnie patrząc na argv [0], argv [1], argv [2].
Więc teraz można zobaczyć, dlaczego to wywołanie strcmp będzie na porażkę,
bo widać, że nie rozstali się na CS50 i skały na dwa odrębne argumenty.
W tym momencie, kiedy został trafiony punkt przerwania, można przejść do kroku za pośrednictwem programu
linia po linii, w przeciwieństwie do uruchamiania programu ponownie.
Więc jeśli nie chcesz, aby uruchomić program ponownie i po prostu dalej od tego miejsca,
możesz użyć polecenia continue i nadal będzie działać program do końca.
Podobnie jak to miało miejsce tutaj.
Jednak, jeśli ponowne uruchomienie programu, CS50 skały, uderza mój przerwania ponownie
i tym razem, jeśli nie chcesz po prostu przejść całą drogę przez resztę programu,
Można użyć następnego polecenia, które ja również skrócić zn.
I to będzie krok po kroku linii programu po linii.
Więc można oglądać jako rzeczy wykonać, jako zmiennych na zmiany, jak rzeczy się aktualizacji.
Które jest bardzo ładne.
Inne fajne jest to, a nie powtarzanie tego samego polecenia w kółko i od nowa,
jeśli po prostu naciśnij Enter - więc widzisz, że nie wpisałeś w nic -
jeśli po prostu naciśnij Enter, będzie powtórzyć poprzednie polecenie,
lub poprzednie polecenie GDB, że po prostu umieścić w.
Mogę naciskamy Enter i będziesz przechowywać Krokowe mojej linii kodu po wierszu.
Chciałbym zachęcić, żebyście go sprawdzić inne programy buggy, jak również.
Nie mamy czasu, żeby ich wszystkich dzisiaj w sekcji.
Kod źródłowy jest tam, więc można trochę zobaczyć, co się dzieje
za kulisami, jeśli się naprawdę zatrzymany,
ale co najmniej, po prostu ćwiczyć uruchamiania GDB,
uruchomieniu programu aż do jego zniszczenia na ciebie,
coraz backtrace, zastanawianie się, jaką funkcję katastrofy był w,
co to było na linii, drukowanie pewne wartości zmiennych,
tylko tak się czuć za to, bo to naprawdę pomóc w przyszłości.
W tym momencie mamy zamiar wyjść z GDB, który można zrobić za pomocą quit lub tylko q.
Jeśli twój program jest w środku działa nadal i nie odszedł,
to zawsze zapytać, "Czy na pewno na pewno chcesz zakończyć?"
Możesz po prostu wciskamy tak.
>> Teraz będziemy patrzeć na kolejny problem, jaki mamy, czyli program cat.
Jeśli oglądasz krótkie na przekierowanie i rur, zobaczysz, że ten program używa Tommy
że w zasadzie drukuje wszystkie dane wyjściowe do pliku na ekranie.
Więc jeśli mogę uruchomić kota, to jest rzeczywiście wbudowany program do urządzenia,
a jeśli masz Mac można to zrobić na komputerze Mac, jeśli chcecie otworzyć terminal.
A my - cat, powiedzmy, cp.c, i naciśnij klawisz Enter.
Co to było, gdybyśmy przewijać się trochę i zobaczyć, gdzie zabrakło nam linię,
lub gdy zabrakło polecenia cat, to dosłownie po prostu wydrukować zawartość cp.c naszym ekranie.
Możemy uruchomić go ponownie i można umieścić w wielu plikach razem.
Więc można zrobić cp.c kota, a następnie możemy złączyć cat.c plik,
co to jest program zamierzamy pisać,
i będzie to wydrukować oba pliki z powrotem do tyłu do naszego ekranu.
Więc jeśli mamy przejść się trochę, widzimy, że kiedy zabrakło tego cp.c kota, cat.c,
Początkowo drukowana cp plik, a następnie pod nią, to wydrukuje cat.c plik aż tutaj.
Zamierzamy to wykorzystać, aby po prostu nasze nogi mokre.
Pobaw się z prostych zadań drukowania do terminalu, zobaczyć, jak to działa.
Jeśli faceci otwarcie z gedit cat.c, naciśnij Enter,
można zobaczyć program, który mamy zamiar pisać.
Zamieściliśmy ten miły talerz kotła, więc nie trzeba tracić czasu na pisanie wszystko to.
Mamy również sprawdzić liczbę argumentów przekazanych w.
Drukujemy z miłą użytkowaniu.
>> To jest coś takiego, że znów, jak rozmawialiśmy o,
to prawie jak pamięci mięśniowej.
Wystarczy pamiętać, aby robić tego samego rodzaju rzeczy
i zawsze drukując jakieś pomocne wiadomości
tak, że ludzie wiedzą, jak uruchomić program.
Z kotem, to całkiem proste, jesteśmy po prostu się przejść przez wszystkie różne argumenty
, które zostały przekazane do naszego programu, a my zamierzamy drukować
ich zawartość uwagę do ekranu jeden na raz.
Aby drukować pliki z do ekranu, mamy zamiar zrobić coś bardzo podobnego
co my w końcu quizu.
Pod koniec quizu, to zatrudnić program, mieliśmy do otwarcia pliku,
a następnie mieliśmy do niej drukować.
W tym przypadku, mamy zamiar otworzyć plik, a będziemy czytać z niej zamiast.
Następnie jedziemy do drukowania, zamiast do pliku, będziemy drukować na ekranie.
Więc wyświetlenie na ekranie, na który wszyscy wykonanej wcześniej z printf.
Tak, że nie jest zbyt szalone.
Ale czytanie pliku jest dziwne.
Pójdziemy przez to trochę mało na raz.
Jeśli faceci wrócić do tego ostatniego problemu na quiz, problem 33,
Pierwszy wiersz, który będziemy robić tutaj, otwierając plik, jest bardzo podobny do tego, co zrobiliśmy tam.
Więc Stella, co robi, że wygląd linii, jak, kiedy otworzyć plik?
[Stella] * FILE Capital, file - >> Ok. >> - Jest równa fopen. >> Tak.
Która w tym przypadku jest? Jest to w komentarzu.
>> To w komentarzu? argv [i] oraz r?
>> Dokładnie. Na prawo. Więc Stella jest całkowicie w porządku.
To, co wygląda jak linia.
Zamierzamy uzyskać zmienną strumienia pliku, zapisać w pliku *, więc wszystkie czapki,
FILE *, oraz nazwę tej zmiennej będzie plik.
Możemy nazwać to, co nam się podoba. Możemy nazwać to first_file lub file_i, cokolwiek byśmy chcieli.
A następnie nazwa pliku została podjęta w wierszu poleceń do tego programu.
Więc to jest przechowywane w argv [i,], a następnie jedziemy do otwarcia tego pliku w trybie odczytu.
Teraz, kiedy otworzył plik, co jest rzeczą, że zawsze musimy pamiętać, aby zrobić
gdy mamy otwarty plik? Zamknij ją.
Więc Missy, w jaki sposób zamknąć plik?
[Missy] fclose (plik) >> fclose (plik). Dokładnie.
Great. Okay. Jeśli spojrzymy na to uwagi komentarz tutaj,
mówi: "Open argv [i] i wydrukować jego zawartość na standardowe wyjście."
>> Obecnie standardem jest dziwne imię. Stdout jest tylko nasz sposób na powiedzenie
chcemy wydrukować go do terminala, chcemy wydrukować go do standardowego strumienia wyjściowego.
Rzeczywiście możemy się pozbyć tego komentarza tutaj.
Idę go skopiować i wkleić go ponieważ to, co zrobiliśmy.
W tym momencie, teraz przeczytać pliku bitu kroku.
Omówiliśmy kilka sposobów czytania plików.
Które z nich są Twoje ulubione do tej pory?
Jakie sposoby widziałaś lub nie pamiętasz, do odczytu plików?
[Daniel] fread? >> Fread? Więc fread jest jeden. Jimmy, czy znacie jakieś inne?
[Jimmy] L. >> Okay. Nope. Charlotte? Alexander? Jakieś inne? Okay.
Inni więc z nich są fgetc, jest jednym, że będziemy korzystać z wielu.
Jest też fscanf; wy patrz wzór tutaj?
Wszyscy zaczynają się f. Coś zrobić z plikiem.
Jest fread, fgetc, fscanf. Są wszystkie funkcje odczytu.
Do pisania mamy fwrite mamy fputc zamiast fgetc.
Mamy również fprintf jak widzieliśmy na quiz.
Ponieważ jest to problem, który obejmuje czytanie z pliku,
mamy zamiar skorzystać z jednej z tych trzech funkcji.
Nie będziemy korzystać z tych funkcji na dole.
Funkcje te są w normie I / O biblioteki.
Więc jeśli spojrzymy na początek programu
widać, że mamy już zawarte w pliku nagłówka dla Standard I / O bibliotece.
Jeśli chcemy dowiedzieć się, który z nich chcemy korzystać,
zawsze możemy otworzyć stron podręcznika.
Tak więc możemy napisać stdio man
i przeczytać o stdio funkcji wejściowych i wyjściowych w C.
I możemy już zobaczyć oh, spójrz. To wspomnieć fgetc, to wspomnieć fputc.
Więc można drążyć trochę i spojrzeć na, powiedzmy, fgetc
i spojrzeć na jego stronie man.
Widać, że to idzie w parze z całą masę innych funkcji:
fgetc, fgets, getc, getchar, dostaje, ungetc, a jej wejście znaków i łańcuchów.
Tak to jest, jak czytamy w znaków i ciągi z plików ze standardowego wejścia,
która jest w zasadzie z użytkownika.
I to jest, jak to robimy w rzeczywistej C.
Więc to nie jest za pomocą funkcji getString i getchar
że kiedyś z CS50 biblioteki.
Mamy zamiar zrobić z tym problemem w kilka sposobów
tak że można zobaczyć dwa różne sposoby to zrobić.
Zarówno funkcja fread Daniel wspomina i fgetc są dobre sposoby, aby to zrobić.
Myślę fgetc jest trochę łatwiej, bo to tylko ma, jak widać,
jeden argument, * FILE, że staramy się odczytać znak z,
, a jego wartość zwracana jest int.
I to jest trochę mylące, prawda?
>> Ponieważ dostajemy znak, więc dlaczego nie ten powrót nie char?
Macie jakieś pomysły, dlaczego to może nie wrócić char?
[Odpowiedzi Missy, niezrozumiały] >> Tak. Więc Missy jest całkowicie w porządku.
Jeśli to jest ASCII, to całkowita może być mapowane do rzeczywistej char.
Może być znak ASCII, i to jest w porządku.
To jest dokładnie to, co się dzieje.
Używamy int tylko dlatego, że ma więcej bitów.
Jest większy niż char; nasz char ma tylko 8 bitów, że 1 bajt na naszych maszynach 32-bitowych.
I int ma warte 4 bajty 'przestrzeni.
I okazuje się, że sposób, fgetc działa
jeśli przewiń w naszym streszczeniu w manualu bitowym mało,
przejść całą drogę w dół. Okazuje się, że używają oni tę szczególną wartość o nazwie EOF.
To specjalny stały jako wartość zwracana z funkcji fgetc
gdy trafisz na koniec pliku lub jeśli pojawia się błąd.
I okazuje się, że aby wykonać te porównania z EOF prawidłowo,
chcesz mieć tę dodatkową ilość informacji, które masz w int
w przeciwieństwie do używania char zmiennej.
Nawet fgetc skutecznie się znak z pliku,
chcesz zapamiętać, że jest coś, co jest powrotem typu int do Ciebie.
Powiedział, że jest to dość łatwe w użyciu.
To da nam znak, więc wszystko, co musimy zrobić, to prosić plik,
"Daj mi następny znak, daj mi kolejny znak, daj mi następnego znaku"
aż dojdziemy do końca pliku.
I to będzie ciągnąć w jednym znaku naraz z naszego pliku,
i możemy robić co chcemy z nim.
Możemy przechowywać, możemy dodać go do łańcucha, możemy wydrukować.
Czy cokolwiek z tego.
>> Powiększanie z powrotem i wraca do naszego cat.c programu
jeśli mamy zamiar używać fgetc,
jak możemy podejść do tego następnego wiersza kodu?
Będziemy używać - fread zrobi coś trochę innego.
I tym razem, po prostu zamierzasz korzystać fgetc dostać jeden znak na raz.
Aby przetworzyć cały plik, co możemy zrobić?
Ile znaków są tam w pliku?
Istnieje wiele. Więc prawdopodobnie chcesz, aby jeden
, a następnie dostać innego i dostać innego i dostać inny.
Jaki algorytm myślisz możemy użyć tutaj?
Jaki rodzaj - [Alexander] w pętli? >> Dokładnie.
Jakiś rodzaj pętli.
W rzeczywistości jest wielkim pętli, w tym przypadku.
I tak jak mówisz, brzmi to tak, jak chcesz pętli w całym pliku,
się postać w czasie.
Wszelkie sugestie na temat, co to może wyglądać?
[Alexander, niezrozumiały]
>> Dobra, tylko powiedz mi, w języku angielskim, co próbujesz zrobić? [Alexander, niezrozumiały]
Więc w tym przypadku, to brzmi jak my po prostu staramy się pętli w całym pliku.
[Alexander] Więc i > -?
Myślę, że rozmiar pliku, prawda? Rozmiar - we'll prostu napisać, że tak.
Rozmiar pliku w danej chwili, i + +.
Tak więc okazuje się, że sposób w jaki to zrobić przy użyciu fgetc, a to jest nowy,
jest, że nie ma łatwego sposobu, aby tylko uzyskać rozmiaru pliku
z tym "sizeof" typu konstrukcji, że już widział.
Gdy używamy tego fgetc funkcji wprowadzamy jakieś
nowy, funky składnia to dla pętli, gdzie zamiast po prostu podstawowy licznik
iść znak po znaku, mamy zamiar wyciągnąć jeden znak na raz,
jeden znak w czasie, jak i sposób wiemy, że jesteśmy na końcu
gdy nie jest już liczyć pewną liczbę znaków,
ale kiedy postać możemy wyciągnąć jest to, że specjalny znak końca pliku.
Tak więc możemy to zrobić - ja nazywam to ch, i mamy zamiar go zainicjować
z naszej pierwszej rozmowy, aby uzyskać pierwszy znak z pliku.
Więc tej części tutaj, to będzie dostać znak z pliku
i zapisać go do zmiennej ch.
Będziemy robić to, aż dojdziemy do końca pliku,
co możemy zrobić, sprawdzając dla postaci nie jest równa tej szczególnej postaci EOF.
A potem zamiast robić ch + +, które po prostu zwiększyć wartość,
więc jeśli czytać A z pliku, kapitału, powiedzmy,
ch + + da nam b, a potem mamy C, a następnie d.
To z pewnością nie to, co chcemy. Co chcemy tutaj
w tym ostatnim trochę się chcemy dostać następny znak z pliku.
>> Więc w jaki sposób możemy uzyskać kolejny znak z pliku?
Jak możemy uzyskać pierwszy znak z pliku?
[Student] fgetfile? >> Fgetc, albo, przepraszam, pan całkowitą rację.
I błędnie go tam. Więc tak.
Tutaj zamiast robić ch + +,
jesteśmy po prostu zadzwonię do fgetc (plik), ponownie
i zapisać wynik w naszym samej zmiennej ch.
[Pytanie Student, niezrozumiały]
>> To jest, gdzie ci faceci plik * są specjalne.
Sposób ich pracy jest to, że - przy pierwszym otwarciu - gdy po raz pierwszy udostępnia tego wywołania fopen,
Plik * skutecznie służy jako wskaźnik na początku pliku.
A następnie za każdym razem wywołać fgetc, porusza się o jeden znak za pośrednictwem pliku.
Jeśli więc nazwać, jesteś inkrementacji wskaźnik pliku o jeden znak.
A kiedy fgetc znowu ruszasz mu inny charakter
a inny charakter i inny charakter i inny charakter.
[Pytanie Student, niezrozumiały] >> I that's - tak.
To trochę tej magii pod maską.
Po prostu zachować inkrementacji przez.
W tym momencie jesteś w stanie właściwie pracować z charakterem.
Więc w jaki sposób możemy drukować na to na ekranie, teraz?
Możemy użyć tej samej rzeczy, że printf używany wcześniej.
Że używaliśmy cały semestr.
Możemy wywołać printf,
i możemy przekazać w postaci tak po prostu.
Innym sposobem na to jest, a nie za pomocą printf i konieczności zrobić ten ciąg formatu,
możemy również skorzystać z jednej z pozostałych funkcji.
Możemy użyć fputc, który drukuje znak na ekranie,
wyjątkiem sytuacji, gdy patrzymy na fputc - pozwól mi oddalić się trochę.
Widzimy to, co miłe jest potrzebny w charakterze że czytamy z wykorzystaniem fgetc,
ale musimy dać mu strumień drukować.
Możemy również użyć putchar funkcję, która położy bezpośrednio wyjście standardowe.
Tak więc istnieje cała masa różnych opcji, które możemy wykorzystać do drukowania.
Oni wszyscy są w standardowym I / O bibliotece.
Ilekroć chcesz drukować - tak printf, domyślnie zostaną wydrukowane w specjalnym standardzie z potoku,
czyli że stdout.
Więc może po prostu o nim jako o rodzaju tej magicznej wartości, stdout tutaj.
Ups. Umieść średnik zewnątrz.
>> To jest dużo nowych, informacji modny tutaj.
Wiele z tego jest bardzo idiomatyczne, w tym sensie, że jest to kod
, co jest napisane w ten sposób tylko dlatego, że jest czysty czytać, łatwe do odczytania.
Istnieje wiele różnych sposobów na to, wiele różnych funkcji, których można użyć,
ale zazwyczaj wystarczy wykonać te same wzory w kółko.
Więc nie zdziw się, jeśli widzisz kod jak to zbliża się ponownie i ponownie.
Dobrze. W tym momencie, że trzeba podzielić na dzień.
Dzięki za przyjście. Dzięki za oglądanie czy jesteś online. I do zobaczenia w przyszłym tygodniu.
[CS50.TV]