[Powered by Google Translate] [Rozdział 5 - bardziej komfortowe] [Rob Bowden - Harvard University] [To jest CS50. - CS50.TV] Jak powiedziałem w moim e-mail, istnieje wiele rzeczy, których można używać inne niż urządzenia faktycznie zrobić zestawy problemów. Zalecamy to zrobić w urządzeniu, tylko dlatego, możemy łatwiej pomóc i wiemy, jak to wszystko będzie działać. Ale jako jeden z przykładów, gdzie można robić rzeczy, jeśli, powiedzmy, że nie mają dostępu do urządzenia lub chcesz pracować w podziemiach Centrum Nauki - które w rzeczywistości mają urządzenia również - jeśli chcesz pracować w dowolnym miejscu. Jednym z przykładów jest widziałeś / słyszałeś SSH? SSH jest w zasadzie tak, jak połączyć się z czegoś. Właściwie teraz jestem SSHed do urządzenia. Nigdy nie pracować bezpośrednio w urządzeniu. Oto urządzenie, i jeśli spojrzeć na dół zobaczyć tego adresu IP. Nigdy nie pracować w samego urządzenia; I zawsze przyjść do iTerm2 oknie terminala / oknie. Możesz SSH do tego adresu IP, ssh jharvard@192.168.129.128. Pamiętam ten numer bardzo łatwo, bo to taki ładny wzór. Ale że poprosi mnie o hasło, a teraz jestem w urządzeniu. Zasadniczo, w tym momencie, jeśli terminal otwarty wewnątrz samego urządzenia, ten interfejs, jednak chcesz go używać, jest dokładnie taka sama jako interfejsu używam tutaj ale teraz SSHed. Nie masz do SSH do urządzenia. Przykładem innego miejsca, w którym mógłby ssh to jestem pewien, masz domyślnie - Oh. Większe. Każdy z was powinien mieć przez FAS domyślnych kont na serwerach FAS. Dla mnie, bym SSH rbowden@nice.fas.harvard.edu. To będzie prosić, że po raz pierwszy, i że tak. Moje hasło jest tylko będzie moje hasło FAS. I tak teraz, jestem SSHed do miłych serwerach, i mogę robić co zechcę tutaj. Wiele klas może podjąć, jak 124, będą musieli przesyłać rzeczy do tego miejsca faktycznie przesłać zestawy problemów. Ale mówisz, że nie masz dostępu do urządzenia. Potem możesz robić rzeczy, jak tu będzie to powiedzieć - To jest po prostu nasza sekcja pytań. Będzie prosić, aby to zrobić w urządzeniu. Zamiast ja po prostu zrobić to na serwerze. Idę do rozpakowania tego. Problem będzie, że jesteś przyzwyczajony do korzystania coś gedit lub cokolwiek wewnątrz urządzenia. Nie będziemy mieć, że na serwerze FAS. To wszystko tylko będzie to tekstowy interfejs. Więc można albo jedno, spróbuj dowiedzieć się edytor tekstu, że mamy. Mają Nano. Nano jest zazwyczaj bardzo proste w użyciu. Możesz użyć strzałek i wpisz normalnie. Więc to nie jest trudne. Jeśli chcesz uzyskać naprawdę fantazyjne można użyć Emacsa, które prawdopodobnie nie powinno otworzyły bo nawet nie wiem jak zamknąć Emacsa. Sterowanie X, Control C? Tak. Albo użyć Vima, która jest ich używam. I tak są to opcje. Jeśli nie chcesz tego zrobić, możesz także, jeśli spojrzeć na manual.cs50.net-- Oh. Na PC można SSH przy użyciu PuTTY, które będziesz musiał pobrać oddzielnie. Na komputerze Mac, można po prostu przez Terminal USE lub możesz pobrać iTerm2, która jest jak miły, Terminal fantazji. Jeśli pójdziesz do manual.cs50.net zobaczysz link do Notepad + +, czyli to, co można używać na komputerze. Pozwala SFTP z Notepad + +, który jest w zasadzie SSH. Co ta pozwoli Ci zrobić to edytować pliki lokalnie, , a następnie, gdy chcesz je zapisać, to zaoszczędzić do nice.fas, gdzie możesz je uruchomić. A odpowiednik na Mac będzie TextWrangler. Więc pozwala zrobić to samo. Pozwala edytować pliki lokalnie i zapisać je do nice.fas, gdzie możesz je uruchomić. Więc jeśli kiedykolwiek zatrzymany bez urządzenia, masz te opcje aby nadal robić swoje zestawy problemów. Jeden problem ma być to, że nie będziemy mieć CS50 biblioteki bo nice.fas domyślnie nie mieć. Możesz pobrać CS50 biblioteki - Nie sądzę, że muszę, że w tym momencie. Możesz pobrać CS50 biblioteki i skopiować go do nice.fas, lub Myślę, że w tym momencie nie używamy go już tak. Albo jeśli to zrobimy, można na razie zastąpić go implementacje funkcji w CS50 biblioteki i tak. Tak, że nie może być o wiele ograniczenia. I to jest to. Wrócę do urządzenia teraz, zrobimy wszystko, co w urządzeniu. Patrząc na naszej sekcji pytań, na początku, jak powiedziałem w moim e-mail, Musimy porozmawiać o jednym krótkim Miałeś oglądać. Mamy przekierowanie i rur i te trzy pytania. Do których nie działa jak strumień printf pisać domyślnie? Więc stream. Co to jest stream? Strumień jest w zasadzie jak to tylko niektóre - To nie jest nawet źródłem 1s i 0s. Strumień to prosząc o to standardowe wyjście. I tak średnia obecnie jest strumień, który podczas pisania do niego, pojawia się na ekranie. Standardowe wyjście, przez strumień, oznacza to po prostu napisać 1s i 0s do niego, , a drugi koniec z standardowej się po prostu z tego strumienia odczytuje. To jest po prostu ciąg 1s i 0s. Możesz napisać do strumieni lub możesz przeczytać ze strumieni w zależności od tego, co faktycznie jest strumień. Pozostałe dwa strumienie domyślne to standardowe i błąd standardowy. Standardem jest, gdy robisz getString, to czeka na ciebie, aby rzeczy wejściowego. Tak więc czekam na Ciebie, to faktycznie czeka na standardzie w, co jest naprawdę to, co dostajesz po wpisaniu na klawiaturze. Piszesz w standardzie w. Błąd standardowy jest w zasadzie odpowiednikiem poza standardowym ale to, że specjalizuje się w przypadku drukowania na standardowym wyjściu błędów, masz się drukować tylko komunikaty o błędach, które więc można odróżnić zwykłe wiadomości wyświetlane na ekranie wobec komunikatów o błędach, w zależności od tego, czy poszedł do wyczerpania standardowego lub błędu standardowego. Pliki. Standardowe wyjście, standard, i standardowy błąd to tylko specjalne strumienie, ale tak naprawdę każdy plik, gdy użytkownik otworzy plik, staje się strumień bajtów gdzie można po prostu odczytać z tego strumienia. Ty, w przeważającej części, można po prostu myśleć o pliku jako strumień bajtów. Więc co oni strumienie zapisu domyślnie? Standardowe wyjście. Jaka jest różnica pomiędzy> i >>? Czy ktoś obejrzeć film wcześniej? Okay. > Będzie jak przekierować do plików, i >> zamierza także przekierować wyjście do plików, ale to zamiast zamiar dodać do pliku. Na przykład, powiedzmy, że zdarza mi się mieć dict tu, i tylko rzeczy wewnątrz dict jest kot, kot, pies, ryba, pies. Jedno polecenie, że trzeba w wierszu poleceń jest kot, który jest właśnie do druku, co jest w pliku. Więc kiedy mówię dict kota, to będzie drukować kota, kot, pies, ryby, psa. To wszystko, kot robi. Oznacza to, że drukowane na standardowe wyjście kot, kot, pies, ryby, psa. Jeśli zamiast tego chcesz przekierowanie do pliku, można użyć> i przekierować ją do tego, co plik. Zadzwonię do pliku plik. Tak więc jeśli ls, zobaczę mam nowy plik o nazwie pliku. I jeśli go otworzyć, to będzie mieć dokładnie to, co kot umieścić w wierszu polecenia. Więc teraz, jeśli to zrobię to jeszcze raz, a potem to się przekierować wyjście do pliku, i mam zamiar mieć dokładnie taki sam rzecz. Więc technicznie, całkowicie zagłuszył to, co mieliśmy. I zobaczymy, jeśli zmienię dict, wyjąłem psa. Teraz jeśli kot dict do pliku ponownie, będziemy mieć nową wersję z psem usunięte. Więc to, że całkowicie przesłania go. Zamiast tego, jeśli używamy >>, to będzie dołączyć plik. Teraz, otwierania pliku, widzimy, mamy tylko samo wydrukowane dwukrotnie dlatego, że był tam kiedyś, to dołączone do oryginału. Więc to> i >> zrobić. Czy następny zapytać - To nie o to zapytać. Druga, że ​​mamy jest <, które jeśli> przekierowuje standardowe wyjście, zrobić 2> który jest przekierowanie standardowego błędu. Więc jeśli coś się do błędu standardowego, to nie byłoby się położyć do txt2. Zauważmy jednak, jeśli mam zrobić 2>, a następnie to nadal drukowanie Witaj, Rob! do linii poleceń bo jestem tylko przekierowanie standardowego błędu, nie jestem przekierowanie standardu zewnątrz. Standardowy błąd i obecnie standardem są różne. Jeśli chciał faktycznie napisać do błędu standardowego, wtedy będę mógł zmienić się fprintf na stderr. Więc printf, domyślnie drukuje na wyjście standardowe. Jeśli chcę wydrukować do błędu standardowego ręcznie, potem użyć fprintf i określić, co chcę drukować. Jeżeli zamiast tego zrobiłem fprintf stdout, to jest to w zasadzie odpowiednikiem printf. Ale fprintf do błędu standardowego. Więc teraz, jeśli mam przekierować to do txt2, Hello, Rob! wciąż się drukowane w linii poleceń ponieważ wychodzi na standardowym błąd i jestem tylko przekierowuje standardowe wyjście. Jeśli teraz przekierować standardowy błąd, teraz nie wydrukowany i txt2 będzie Witaj, Rob! Więc teraz, możesz drukować swoje rzeczywiste błędy błędu standardowego i drukować regularne komunikaty out standardowe. I tak po uruchomieniu programu, można uruchomić ją. / Hello tego typu z 2> tak, że program będzie działać normalnie, ale komunikaty o błędach, które cię mogą sprawdzić później w dzienniku błędów, więc błędów, a następnie szukać później i plik błędy nie będą miały żadnych błędów, które się wydarzyły. Questions? Ostatnia jest pipe, które można myśleć jak biorąc standard się z jednego polecenia i co to standard w następnej komendy. Przykładem jest tu echo jest rzeczą wiersza poleceń , który jest po prostu będzie echo cokolwiek umieścić jako argumentu. I nie będzie umieścić cudzysłów. Echo bla, bla, bla, jest właśnie do druku bla, bla, bla. Wcześniej, kiedy powiedział, że musiał włożyć Roba do pliku txt bo mogę tylko przekierowanie plików txt, zamiast / if echo Roba a następnie go do rury. / hello, że zrobi ten sam rodzaj rzeczy. To trwa wyjście tego polecenia, echo Rob, i używanie go jako wejście do. / hello. Można myśleć o tym jako pierwszy przekierowanie echa Roba do pliku a następnie wprowadzić do. / hello tym pliku, który właśnie wyprowadzać. Ale to wymaga tymczasowego pliku z obrazem. Pytania na temat tego? Następne pytanie będzie dotyczyć tego. Co rurociąg można użyć, aby znaleźć liczbę unikatowych nazw w pliku o nazwie names.txt? Polecenia zamierzamy użyć tutaj są wyjątkowe, tak uniq, a następnie wc. Możesz zrobić uniq mężczyzna rzeczywiście patrzeć na to co, że nie, i to tylko będzie filtrować sąsiadujące pasujące linie z wejścia. I człowiek wc będzie wydrukować znak nowej linii, słów i bajtów liczy się dla każdego pliku. I ostatni zamierzamy użyć jest sortowanie, który będzie po prostu posortować wiersze pliku txt. Jeśli zrobię jakiś plik txt, names.txt, i to jest Rob, Tommy, Joseph, Tommy, Joseph, RJ, Rob, co chcę zrobić tutaj jest znaleźć liczbę unikatowych nazw w tym pliku. Więc co należy odpowiedź? >> [Uczeń] 4. >> Tak. Powinno być 4 od Rob, Tommy, Józefa, RJ to jedyne unikalne nazwy w tym pliku. Pierwszy krok, jeśli po prostu liczą na names.txt słowo, To jest rzeczywiście mówi mi wszystko. To jest rzeczywiście druk - Zobaczmy, man wc - newlines, słowa i liczyć bajt. Gdybym tylko o linii, to może po prostu zrobić wc-l names.txt. Więc to jest etap 1. Ale ja nie chcę wc-l names.txt ponieważ names.txt tylko zawiera wszystkie nazwiska, i chcę, aby odfiltrować nie-niepowtarzalne. Więc jeśli ja names.txt uniq, że nie za bardzo da mi to, co chcę ponieważ zduplikowane nazwy są nadal. Dlaczego tak jest? Dlaczego nie uniq robić to, co chcę? [Uczeń] duplikaty nie są [niesłyszalne] >> Tak. Zapamiętaj na stronie man uniq mówi filtr sąsiadujące pasujące linie. Oni nie są w sąsiedztwie, więc nie będzie filtrować je. Jeśli sortować je najpierw names.txt sort będzie umieścić wszystkie zduplikowane linie razem. Więc teraz names.txt sort jest. Mam zamiar użyć, że wejście do uniq, co jest | uniq. To daje mi Józefa, RJ, Rob, Tommy, i chcę, aby użyć jej jako wejście do wc-l, co da mi 4. Jak tu jest napisane, co rurociąg można użyć? Można zrobić wiele rzeczy, jak przy użyciu serię poleceń gdzie wykorzystać wyjście z jednego polecenia jako wejście do następnego polecenia. Można zrobić wiele rzeczy, wiele mądrych rzeczy. Questions? Okay. To jest to dla rur i przekierowania. Teraz możemy przejść do rzeczy, rzeczywistych rzeczy kodowania. Wewnątrz to PDF, zobaczysz tego polecenia, i będziesz chciał uruchomić tą komendę w swoim urządzeniu. wget jest polecenia dla dopiero się coś z internetu, w zasadzie, więc wget i to URL. Jeśli poszedł do tego adresu URL w przeglądarce, by go pobrać ten plik. I właśnie kliknął na niego, więc to pobrany plik dla mnie. Ale pisanie wget tej rzeczy wewnątrz terminalu jest po prostu się go ściągnąć do terminalu. Mam section5.zip, i będziemy chcieli, aby rozpakować section5.zip, która będzie Ci folder o nazwie section5, który będzie miał wszystkie pliki zamierzamy używać dziś w jej wnętrzu. Jak nazwy tych programów plików sugerują, że są nieco buggy, więc Twoim zadaniem jest dowiedzieć się, dlaczego przy użyciu gdb. Czy każdy musi je pobrać / wiedzieć, jak uzyskać ich pobraniu do swojego urządzenia? Okay. Bieganie ./buggy1, to powie Segmentation fault (core dumped) które w każdej chwili można uzyskać segfault, to jest złe. W jakich okolicznościach dostajesz segfault? [Uczeń] dereferencji wskaźnika pustego. >> Tak. Tak, że jeden z przykładów. Dereferencji pustego wskaźnika masz zamiar dostać segfault. Co segfault oznacza to jesteś dotykając pamięci nie należy dotykać. Więc dereferencji pustego wskaźnika dotyka adres 0, iw zasadzie wszystkie komputery w dzisiejszych czasach, że 0 adres pamięci nie należy dotykać. Więc dlatego dereferencji wskaźnika null wyniki w segfault. Gdy zdarzy Ci się nie zainicjować wskaźnik, to ma wartość śmieci, i tak przy próbie dereference to, najprawdopodobniej jesteś dotykając pamięć , że jest w środku pustkowia. Jeśli zdarzy ci się mieć szczęście i wartość śmieci się wskazywać na gdzieś na stosie czy coś, następnie po dereference że wskaźnik, które nie zostały zainicjowane, nic się nie udać. Ale jeśli jest skierowany do, powiedzmy, gdzieś między stosu i sterty, czy to wskazuje tylko gdzieś, że nie był używany przez program jeszcze, to jesteś dotykając pamięci nie należy dotykać i segfault. Kiedy piszesz funkcji rekurencyjnej i recurses zbyt wiele razy a stos rośnie zbyt duży i zderza stosu do rzeczy że nie powinno być kolizji z, jesteś dotykając pamięci nie należy dotykać, więc segfault. To, co segfault jest. To również z tego samego powodu, że jeśli ciąg jak - wróćmy do poprzedniego programu. W hello.c--jestem po prostu się do czegoś innego. char * s = "hello world!"; Jeśli używam * s = coś lub s [0] = 'X'; należy więc witam. / hello, dlaczego że segfault? Dlaczego to segfault? Czego można się spodziewać się stanie? Jeżeli zrobiłem printf ("% s \ n", s); co można się spodziewać do druku? [Uczeń] X hello. >> Tak. Problem jest, że kiedy zadeklarować ciąg jak to, s jest wskaźnik, który pójdzie na stos, i co s wskazuje na to łańcuch, który jest zawarty w pamięci tylko do odczytu. Więc po prostu nazwą, pamięci tylko do odczytu, należy się pomysł że jeśli chcesz zmienić to, co znajduje się w pamięci tylko do odczytu, robisz coś, czego nie powinien robić z pamięcią i segfault. To jest rzeczywiście wielka różnica między char * s, a char s []. Więc char s [], teraz ten ciąg ma być położony na stosie, i stos nie jest tylko do odczytu, co oznacza, że ​​powinno to działać całkiem dobrze. I to robi. Pamiętaj, że gdy zrobić char * s = "Hello World!", S sam w sobie jest na stosie ale s wskazuje gdzieś indziej, i że gdzieś dzieje się tylko do odczytu. Ale char s [] jest po prostu coś na stosie. Więc to jest kolejny przykład segfault dzieje. Widzieliśmy, że ./buggy1 spowodowało segfault. Teoretycznie nie powinno się patrzeć na buggy1.c natychmiast. Zamiast tego, będziemy patrzeć na to przez gdb. Zauważ, że gdy pojawi się Segmentation fault (core dumped) masz ten plik tutaj zwanego rdzenia. Jeśli ls-l, to widzimy, że rdzeń jest zazwyczaj dość duży plik. Jest to liczba bajtów pliku, więc wygląda na to, że to 250-coś kilobajtów. Powodem tego jest, że to, co faktycznie jest zrzutu jest, gdy twoje awarii programu, stan pamięci programu tylko zostanie skopiowana i wklejona do tego pliku. Zostaje skierowana do tego pliku. Program ten, gdy był uruchomiony, stało się mieć użycie pamięci około 250 kilobajtów, i tak, że to, co mnie wrzucano do tego pliku. Możesz teraz spojrzeć na plik jeśli robimy gdb buggy1 rdzeń. Możemy tylko zrobić gdb buggy1, i że będzie po prostu uruchomić gdb regularnie używając buggy1 jako pliku wejściowego. Ale jeśli nie gdb buggy1 rdzeń, to jest to konkretnie zamierza uruchomić gdb patrząc na ten plik jądra. I mówisz buggy1 dochodowe gdb wie, że plik pochodzi z rdzenia buggy1 programu. Więc gdb buggy1 rdzeń będzie natychmiast przynieść nam w którym program się zakończyć. Widzimy tutaj Program zakończony sygnałem 11, Segmentation fault. Zdarzy nam się zobaczyć linię montażu, które prawdopodobnie nie jest bardzo pomocne. Ale jeśli wpiszesz BT lub prześledzić, że będzie to funkcja to daje nam listę naszych aktualnych ramek stosu. Więc backtrace. Wygląda na to, że tylko dwa stosu ramek. Pierwszy to nasz główny ramki stosu, a drugi stack frame dla tej funkcji, które przebywają w, która wygląda jak mamy tylko kod montażowa. Więc wróćmy do naszej głównej funkcji, a do tego nie możemy zrobić ramkę 1 i myślę, że możemy również zrobić w dół, ale prawie nigdy nie zrobić dół - lub w górę. Tak. W górę iw dół. Up przynosi jedną ramkę stosu, aż cię sprowadza w dół ramki stosu. Staram się nie używać. Właśnie specjalnie powiedzieć rama 1, który jest pójść do klatki z etykietą 1. Rama 1 przyniesie nas do głównej ramki stosu, i mówi tu wiersz kodu my będziemy na. Jeśli chcieliśmy jeszcze kilka linijek kodu, możemy powiedzieć, listy, a to da nam wszystkie linie kodu wokół niego. Linia my segfaulted co było 6: if (strcmp ("CS50 rocks", argv [1]) == 0). Jeśli nie jest to oczywiste, ale można dostać prosto z tu tylko przez myślenie dlaczego segfaulted. Ale możemy go o jeden krok dalej i powiedzieć: "Dlaczego argv [1] segfault?" Print niech argv [1], i to wygląda jakby 0x0, co jest wskaźnikiem NULL. Jesteśmy strcmping CS50 skały i Null, i tak, że zamierza segfault. I dlaczego jest argv [1] null? [Uczeń] Ponieważ nie dać mu żadnych argumentów wiersza polecenia. Tak. Nie dać mu żadnych argumentów wiersza polecenia. Więc ./buggy1 tylko będzie mieć argv [0] będzie ./buggy1. To nie będzie mieć argv [1], tak, że zamierza segfault. Ale jeśli zamiast tego zrobić tylko CS50, to powie Dostajesz D ponieważ to, co to ma robić. Patrząc na buggy1.c, to ma się drukować "Dostajesz D" - Jeśli argv [1] nie jest "CS50 skały", "Dostajesz D", inaczej "Masz!" Więc jeśli chcemy, musimy to porównanie za prawdziwe, co oznacza, że ​​porównuje się do 0. Więc argv [1] musi być "CS50 skały". Jeśli chcesz to zrobić z wiersza poleceń, należy użyć \ uciec z miejsca. Więc CS50 \ skały i masz A! Jeśli tego nie zrobisz ukośnika, dlaczego to nie działa? [Uczeń] To dwa różne argumenty. >> Tak. Argv [1] będzie CS50, a argv [2] będzie skał. Okay. Teraz ./buggy2 będzie segfault ponownie. Zamiast otwierać je swoim pliku rdzenia, po prostu otworzyć buggy2 bezpośrednio, tak gdb buggy2. Teraz jeśli tylko uruchomić nasz program, to jest to powie Program otrzymał sygnał SIGSEGV, który jest segfault sygnał, i to, gdzie to się stało, aby się stało. Patrząc na backtrace, widzimy, że byliśmy w oh_no funkcyjnego, który został powołany przez Dinky funkcji, który został nazwany przez Binky funkcyjnego, który został powołany przez głównego. Widzimy również argumenty do tych funkcji. Argument Dinky i Binky był 1. Jeśli podajemy funkcję oh_no, widzimy, że jest po prostu robi oh_no char ** s = null; * S = "BOOM"; Dlaczego miałoby to się nie uda? [Uczeń] Nie można dereference zerowy wskaźnik? >> Tak. To jest po prostu mówiąc s jest równe NULL, niezależnie czy to dzieje się char **, które, w zależności od sposobu jego interpretacji, to może być wskaźnik do wskaźnika do łańcucha lub tablica łańcuchów. To s jest NULL, więc * s jest dereferencji wskaźnika pustego, i tak to będzie się zawieszał. Jest to jeden z najszybszych sposobów można ewentualnie segfault. To jest po prostu stwierdzenie wskaźnika pustego i natychmiast segfaulting. To właśnie oh_no robi. Jeśli idziemy jedną klatkę, a następnie jedziemy do dostać się do funkcji, która wywołała oh_no. Muszę zrobić to. Jeśli nie wprowadzić polecenie i po prostu naciśnij Enter ponownie, będzie po prostu powtórzyć poprzednie polecenie, że uciekł. Jesteśmy w 1 ramy. Wymienienie tej ramki, widzimy tutaj jest nasza funkcja. Można trafić listę ponownie, lub możesz zrobić listę 20 i będzie zawierać więcej. Przemiły funkcja mówi, że jeśli i jest 1, a następnie przejść do funkcji oh_no, jeszcze pójść do Slinky funkcji. I wiem, że jest 1, bo stało się tutaj, aby zobaczyć że przemiły została wywołana z argumentem 1. Można też po prostu są drukowane i będę to powiedzieć, że jest 1. Jesteśmy obecnie w Dinky, a jeśli idziemy inną ramkę, wiemy, będziemy w końcu w Binky. Up. Teraz jesteśmy w Binky. Wymieniając tę ​​funkcję - listy z przed pół przerwała mi - Zaczęło się tak, jakbym to 0, a następnie będziemy nazywać oh_no, inaczej nazwać przemiły. Wiemy, byłem 1, więc wezwał przemiły. A teraz wracamy do głównego, a głównym jest tylko będzie int i = rand ()% 3; To jest po prostu dać ci losowy numer, który jest albo 0, 1 lub 2. To się nazywają Binky z tym numerem, a powróci 0. Patrząc na to, spacerach z programu ręcznie bez uruchamiania go natychmiast należy ustawić punkt przerwy w głównych, co oznacza, że ​​podczas uruchamiania programu Twój program uruchamia się, dopóki nie natrafi na punkt przerwania. Tak więc uruchomienie programu, to będzie działać i wtedy uderzył w głównej funkcji i przestanie działać. Teraz jesteśmy w środku główny i krok lub następny przyniesie nas do następnego wiersza kodu. Możesz zrobić krok lub następnego. Uderzając obok, teraz został ustawiony na rand ()% 3, więc możemy drukować wartość zmiennej, i powie I jest 1. Teraz to ma znaczenie, czy używamy następnego lub krok. Myślę, że chodziło w poprzednim, ale chcemy wykorzystać następny. Jeśli używamy krok, wychodzimy do funkcji, co oznacza, spojrzeć na coś rzeczywistego że dzieje się wewnątrz Binky. Jeśli używamy obok, to znaczy iść na funkcję i po prostu przejść do następnego wiersza kodu w naszej głównej funkcji. Tutaj na tej linii, byłem w którym powiedział, rand ()% 3; jeśli zrobiłem krok, byłoby to do wdrażania rand i spojrzeć na to, co się tam dzieje, i mogłem przejść przez funkcję rand. Ale nie dbam o funkcji rand. Chcę tylko, aby przejść do następnego wiersza kodu w głównym, więc używam dalej. Ale teraz dbam o Binky funkcji, więc chcę wkraczać do tego. Teraz jestem w Binky. Pierwsza linia kodu powie if (i == 0), to krok, widzimy, skończymy na Dinky. Jeśli my, lista rzeczy, widzimy, że jest to sprawdzone i = 0. i nie jest równa 0, więc udaliśmy się do innego stanu, który będzie wywołać przemiły (i). Możesz się mylić. Jeśli patrzeć tylko na tych liniach bezpośrednio, można by pomyśleć, if (i == 0), ok, to ja się o krok, a teraz jestem w Dinky (i), można by pomyśleć, że musi oznaczać i = 0 lub coś. Nie, to po prostu oznacza, że ​​wie, może przykleić bezpośrednio do Dinky linii (i). Bo nie jest 0, następny krok nie skończy się na inny. Else nie linia to się zatrzyma jest. To po prostu się przejść do następnej linii może rzeczywiście wykonać, który jest przemiły (i). Wkraczającego Dinky (i), zobaczymy, czy (i == 1). Wiemy, i = 1, więc gdy wychodzimy, wiemy, mamy zamiar skończyć w oh_no bo = 1 nazywa oh_no funkcji, które można wejść do, który będzie ustawiony char ** s = NULL i natychmiast "boom". I wtedy rzeczywiście patrząc na realizację buggy2, to, i jest tylko coraz losowy numer - 0, 1, lub 2 - wywołanie Binky, co jeśli i jest 0 wywołuje oh_no, w przeciwnym wypadku zwraca przemiły, który pojawia się tutaj. Jeśli i jest 1, call oh_no, inaczej nazwać Slinky, który zbliża się tu, jeśli i jest 2, zadzwoń oh_no. Ja nawet nie myślę, że jest sposób - Czy ktoś widział sposób dokonywania to program, który nie będzie się wysypać? Bo chyba jestem brakuje czegoś, jeżeli i jest 0, będziesz natychmiast segfault, jeszcze udać się do funkcji, która jest 1 jeśli i wy segfault, jeszcze udać się do funkcji, gdzie jeśli i jest 2 Państwo segfault. Więc nie ma znaczenia, co robisz, segfault. Chyba jeden sposób mocowania byłoby zamiast robić char ** s = null, można przydzielić miejsca na ten ciąg znaków. Moglibyśmy zrobić malloc (sizeof) - sizeof co? [Uczeń] (char) * 5? >> Czy to wydaje się słuszne? Jestem zakładając, że to zadziała, jeśli faktycznie zabrakło, ale to nie to co szukam. Zobacz na typu S. Dodajmy int *, więc int * x. Chciałbym zrobić malloc (sizeof (int)). Albo gdybym chciał tablicy 5, chciałbym zrobić (sizeof (int) * 5); Co jeśli mam int **? Co bym malloc? [Uczeń] Wielkość wskaźnika. >> Tak. (Sizeof (int *)); To samo tutaj. Chcę (sizeof (char *)); To będzie przydzielić miejsce dla wskaźnika, który wskazuje na "boom". I nie trzeba przydzielić miejsce dla "boom" sam bo to jest w zasadzie odpowiednikiem tego, co powiedziałem wcześniej char * x = "BOOM". "Boom" już istnieje. Dzieje się tak, aby występować w obszarze tylko do odczytu z pamięci. Ale to już istnieje, co oznacza, że ​​ten wiersz kodu, jeśli s jest char **, następnie * s jest char * i jesteś ustawienie tego char * wskazać na "boom". Gdybym chciał skopiować "boom" w S, to musiałbym przeznaczyć przestrzeń dla tych. Zrobię * s = malloc (sizeof (char) * 5); Dlaczego 5? Dlaczego nie 4? To wygląda jak "boom" jest 4 znaków. >> [Uczeń] znak null. Tak. Wszystkie Twoje ciągi będą potrzebne znak null. Teraz można zrobić coś jak strcat - Co to jest funkcja kopiowania ciąg? [Uczeń] CPY? >> Strcpy. strcpy człowiek. Więc strcpy lub strncpy. strncpy jest nieco bezpieczniejsze, ponieważ można dokładnie określić, ile znaków, ale tutaj to nie ma znaczenia, bo wiemy. Więc strcpy i patrzeć w argumentach. Pierwszy argument jest nasz cel. Drugi argument jest nasze źródło. Mamy zamiar skopiować do naszego docelowego * s wskaźnik "BOOM". Dlaczego warto to zrobić z strcpy zamiast po prostu to, co mieliśmy przed z * s = "BOOM"? Jest powód, możesz to zrobić, ale co to jest powód? [Uczeń] Jeśli chcesz coś zmienić w "boom". >> Tak. Teraz mogę zrobić coś jak s [0] = 'X'; bo s wskazuje na stertę i że przestrzeń na stercie, że s jest skierowana do jest wskaźnikiem do więcej miejsca na stercie, który jest przechowywanie "boom". Więc to kopia "boom" jest przechowywana w stercie. Jest technicznie dwa egzemplarze "boom" w naszym programie. Jest pierwszym, który jest po prostu podane przez "Boom" stałej strun i drugi egzemplarz "boom", strcpy stworzył kopię "boom". Ale kopia "boom" jest przechowywana na stercie, a sterty jesteś wolny, aby zmienić. Kupa nie jest tylko do odczytu, więc to oznacza, że ​​s [0] będzie pozwalają zmienić wartość "boom". To będzie pozwalają zmienić te znaki. Questions? Okay. Przechodząc do buggy3, niech gdb buggy3. Po prostu uruchom go i widzimy mamy segfault. Jeśli backtrace, istnieją tylko dwie funkcje. Jeśli udamy się do naszej głównej funkcji, widzimy, że segfaulted na tej linii. Więc patrząc na tej linii, for (int wiersz = 0; fgets to rzeczy nie jest równa NULL; linia + +). Nasza poprzednia ramka nazwano _IO_fgets. Zobaczysz, że wiele z wbudowanych funkcji w języku C, że gdy pojawi się wysypać, nie będzie naprawdę zagadkowe nazwy funkcji podobnych _IO_fgets. Ale to będzie się odnosić do tego zaproszenia fgets. Gdzieś tutaj, jesteśmy segfaulting. Jeśli spojrzymy na argumenty do fgets, możemy wydrukować bufor. Miejmy wydrukować jako - Och, nie. Wydruku nie będzie działać dokładnie tak, jak chcemy. Spójrzmy na bieżącym programie. Bufor jest tablica znaków. Jest to tablica znaków z 128 znaków. Więc kiedy mówię, bufor wydruku, to będzie wydrukować te 128 znaków, co chyba jest to, co ma. Co szukałem to wydrukować adres bufora, ale to naprawdę nie ma mi powiedzieć wiele. Tak więc, gdy zdarza mi się powiedzieć tutaj bufor x, to pokazuje mi 0xbffff090, które, jeśli pamiętasz z wcześniejszych lub pewnym momencie Oxbffff bywa stos-owski region. Stos zmierza gdzieś zacząć tuż pod 0xc000. Po prostu widząc ten adres, wiem, że bufor dzieje się na stosie. Restartowanie mój program, uruchom, w górę, buforować widzieliśmy, był to ciąg znaków które są prawie bez znaczenia. Następnie wydrukować plik, co robi plik wyglądał? [Uczeń] Null. >> Tak. Plik jest z FILE * typu, więc jest to wskaźnik, a wartość tego wskaźnika jest null. Więc fgets będzie spróbować odczytać z tego wskaźnika w sposób pośredni, ale w celu uzyskania dostępu do tego wskaźnika, musi dereference go. Lub, w celu uzyskania dostępu do tego, co należy wskazując, to dereferences IT. Więc to dereferencji wskaźnika pustego i to naruszenia ochrony pamięci. Mógłbym wznowił go tam. Jeśli łamiemy na nasz główny punkt i uruchomić, Pierwsza linia kodu jest char * filename = "nonexistent.txt"; To powinno dać dość dużą wskazówkę, dlaczego ten program się nie powiedzie. Wpisując obok prowadzi mnie do następnej linii, gdzie otworzyć ten plik, , a następnie od razu dostać się do naszej linii, gdzie po I uderzył obok, to się wysypać. Czy ktoś chce wyrzucić powód dlaczego możemy być segfaulting? [Uczeń] Plik nie istnieje. >> Tak. To ma być podpowiedź że gdy otwieramy plik trzeba sprawdzić, że plik rzeczywiście istnieje. Więc, "nonexistent.txt"; Kiedy filename fopen do czytania, to wtedy trzeba powiedzieć, if (plik == NULL) i powiedzieć, printf ("Plik nie istnieje!" lub - jeszcze lepiej - filename); return 1; Więc teraz sprawdzić, czy to jest NULL zanim rzeczywiście trwają i próbuje odczytać z tego pliku. Możemy przerobić go po prostu zobaczyć, że to działa. I ma obejmować nowy wiersz. Więc teraz nonexistent.txt nie istnieje. Zawsze należy sprawdzić tego typu rzeczy. Należy zawsze sprawdzić, czy fopen zwraca wartość NULL. Zawsze należy sprawdzić, aby upewnić się, że malloc nie zwraca NULL, albo masz segfault. Teraz buggy4.c. Uruchomiony. Zgaduję, to czeka na wejście lub zapętlenie ewentualnie nieskończonym. Tak, to jest nieskończone zapętlenie. Więc buggy4. Wygląda na to, że jesteśmy nieskończone zapętlenie. Możemy przełamać w main, uruchomić nasz program. W gdb, dopóki skrót używasz jest jednoznaczna lub specjalne skróty, które świadczą dla Ciebie, następnie można użyć n użyć następny zamiast wpisywać się obok przez całą drogę. I teraz, kiedy został trafiony n raz, można po prostu naciśnij Enter, aby nie poddawać się następny zamiast trafić n Enter, n Wpisz, n Enter. Wygląda na to, że jestem w jakiejś pętli for, która jest ustawiona tablica [i] do 0. Wygląda na to, nigdy nie jestem wyrwanie się z tego dla pętli. Jeśli mogę wydrukować i, więc jest o 2, potem pójdę dalej. Będę print i, i jest 3, potem pójdę dalej. Będę drukować I i I jest 3. Następnie wydrukować i, i jest 4. Właściwie print sizeof (tablica), więc rozmiar tablicy jest 20. Ale wygląda na to jest jakaś specjalna komenda gdb aby jechać aż coś się wydarzy. To jak ustawienie warunku na wartość zmiennej. Ale nie pamiętam, co to jest. Jeśli więc nie poddawać się - Co pan mówi? Czego się wychować? [Uczeń] Nie wyświetla dodam - >> Tak. Więc wyświetlić mogę pomóc. Jeśli po prostu wyświetlić i, będzie można umieścić tutaj co wartość i jest więc nie trzeba go wydrukować za każdym razem. Jeśli tylko poddawać się Następnie patrz 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5. Coś się bardzo źle, a ja jest resetowane do 0. Patrząc na buggy4.c widzimy wszystko co się dzieje to int tablica [5]; for (i = 0; i <= sizeof (tablica); i + +) tablica [i] = 0; Co widzimy, że to źle? Jako podpowiedź, gdy robiła gdb buggy4 - Złammy main, run - Zrobiłem print sizeof (tablica), by zobaczyć, co jest warunkiem, gdzie powinien wreszcie wyrwać. Gdzie ja jestem? Czy mogę uruchomić? I nie zadeklarował jeszcze. Więc drukować sizeof (array) i to jest 20, który ma ponieważ moja tablica ma rozmiar 5 i to z 5 liczb całkowitych, więc cała sprawa powinna być 5 * sizeof (int) bajtów, gdzie sizeof (int) jest raczej 4. Więc sizeof (array) jest 20. Jakie powinny być? [Uczeń] podzielone przez sizeof (int). >> Tak, / sizeof (int). Wygląda na to, że nadal jest problem. Myślę, że to powinno być po prostu < ponieważ jest to prawie zawsze > [Bowden] Tak. Kiedy jesteśmy poza koniec naszej tablicy, jakoś, że przestrzeń, że jesteśmy nadrzędnego jest nadrzędne wartości i. I tak, jeśli spojrzymy na buggy4, złamać główny, bieg, niech wydrukować adres i. Wygląda na to, że to bffff124. Teraz drukowanie adresu tablicy [0]. 110. Co o [1]? 114. [2], 118. 11c, 120. tablica [5] jest bfff124. Więc tablica [5] ma taki sam adres jak ja, co oznacza, że ​​tablica [5] i. Jeśli mają taki sam adres, są one tak samo. Więc kiedy możemy ustawić tablicę [5] do 0, więc ustawienie I do 0. A jeśli myślisz o tym w warunkach stosu, int i jest zadeklarowany pierwszy, co oznacza, że ​​ma trochę miejsca na stosie. Następnie tablica [5] jest rozdzielona, ​​tak to 20 bajty są alokowane na stosie. Więc zostanie przyznane pierwsze, to te 20 bajtów uzyskać przydzielone. Więc dzieje się tuż przed tablicy, i ze względu na sposób, jak powiedziałem w zeszłym tygodniu, gdy jest to technicznie stos rośnie w dół, kiedy indeks do tablicy, mamy zagwarantowane, że 0-sza pozycji w tablicy zawsze dzieje się przed pierwszą pozycję w tablicy. Jest to rodzaj jak narysowałem to w zeszłym tygodniu. Zauważ, że na dole mamy adres 0 i na górze mamy Max adresów. Stos jest zawsze rośnie w dół. Załóżmy, że mamy przeznaczyć i. Przydzielamy całkowitą i, co oznacza po prostu powiedzieć, tu całkowita i dostaje przydzielone. Następnie przydziela naszej tablicy 5 całkowite, co oznacza, że, pod ponieważ stos rośnie w dół, te 5 całkowitymi się przydzielone. Ale ze względu na to, jak działają zespoły, mamy gwarancję, że pierwsza pozycja w tablicy Adres zawsze mniejsza niż drugiej rzeczy w tablicy. Więc 0 pozycji tablicy zawsze ma się wydarzyć najpierw w pamięci, natomiast pozycja tablica 1 musi się zdarzyć, że po i położenie tablicy 2 ma nastąpić po tym, co oznacza, że ​​pozycja 0 tablica by się stało, gdzieś tutaj, Pozycja tablicy 1 powyżej, że stałoby się ponieważ przesuwa się w górę oznacza wyższe adresy ponieważ maksymalna adres jest tutaj. Więc tablica [0] tu, tablica [1] tu, tablica [2] tu, tablica [3] tutaj. Zauważ, jak zanim przydzielono liczbę całkowitą i całą drogę aż tutaj, jak iść dalej i dalej w naszej tablicy, jesteśmy coraz bliżej do naszej całkowitej i. Tak się składa, że ​​tablica [5], który jest o jedną pozycję poza naszą tablicę, jest dokładnie tam, gdzie całkowita Zdarzyło mi się być przydzielone. Więc to jest punkt, w którym my będziemy uderzając miejsca na stosie , która została przeznaczona na całkowitą i, i ruszamy, że do 0. To, w jaki sposób działa. Questions? Tak. [Uczeń] Nieważne. Okay. [Uczeń] Jak uniknąć tych rodzaju błędów? Są swego rodzaju błędów? Nie stosować C jako języka programowania. Używać języka, który ma granice tablicy kontroli. Tak długo, jak jesteś ostrożny, wystarczy, aby uniknąć mijając granice swojej tablicy. [Uczeń] Więc kiedy poszliśmy przeszłości granice swojej tablicy - [Bowden] To miejsce, gdzie wszystko zaczyna nie tak. >> [Uczeń] Oh, w porządku. Jak długo pozostać w pamięci przydzielonej dla Twojej tablicy, jesteś w porządku. Ale C nie wykonuje sprawdzanie błędów. Jeśli zrobić tablicę [1000], to chętnie tylko zmodyfikować, co się dzieje - Przechodzi do początku tablicy, a następnie idzie od pozycji 1000 i ustawia się na 0. To nie robi żadnej kontroli, tak och, to rzeczywiście nie ma 1000 rzeczy w nim. 1000 jest poza co mam się zmieniać, mając na uwadze, Java czy coś dostaniesz tablicę z indeksu bounds lub indeks z wyjątkiem bounds. Dlatego wiele języków wyższych poziomów te rzeczy gdzie jeśli wykracza poza granice tablicy, nie uda tak, że nie można zmienić rzeczy spod Ciebie a potem wszystko pójdzie znacznie gorzej niż tylko coraz wyjątek mówiąc, że wyszedł poza koniec tablicy. [Uczeń] I tak powinno właśnie zmienił <= po prostu > [Bowden] Tak. Powinien on być > [Uczeń] Racja. Więcej pytań? Okay. [Uczeń] Mam pytanie. >> Tak. [Uczeń] Co to jest rzeczywista zmiennej tablicy? [Bowden] Jak to, co jest tablica? Tablica sama w sobie jest symbolem. To jest po prostu adres rozpoczęcia 20 bajtów, które odwołują. Można traktować ją jako wskaźnik, ale jest stałym wskaźnikiem. Jak tylko robi się skompilowany, zmienna tablica już nie istnieje. [Uczeń] Więc jak to znaleźć rozmiar tablicy? Wielkość odnosi się do tablicy wielkości tego bloku, że symbol oznacza. Kiedy zrobić coś jak printf ("% p \ n", tablica); niech go uruchomić. Co ja zrobiłem źle? Array 'array' zgłaszać tutaj. O, tutaj. Clang jest mądry, a zdarza się, aby zauważyć, że oświadczyłem tablicę jako 5 elementów ale jestem do pozycji 1000 indeksowania. Może to zrobić, ponieważ są to tylko stałe. Można tylko iść tak daleko w zauważając, że będę poza granice tablicy. Zauważmy jednak, wcześniej, kiedy mieliśmy i są błędne, Nie można ewentualnie określić ile wartości mogłem podjąć, tak, że nie można ustalić, że był poza koniec tablicy. To tylko Clang będąc zdolny. Ale teraz zrobić buggy4. Co jeszcze robię źle? Pośrednio deklarowania funkcji biblioteki "printf". Będę chciał # include. Okay. Teraz pracuje buggy4. Drukowanie wartości tablicy jak ja tutaj, drukowania go jako wskaźnik coś drukuje, który wygląda tak - bfb8805c - co jakiś adres To w tym regionie stack-owski. Array sama jest jak wskaźnik, ale to nie jest rzeczywisty wskaźnik, od zwykłego wskaźnika możemy zmienić. Tablica to tylko niektóre stały. Do 20 bloków pamięci zaczynają się 0xbfb8805c adresowej. Więc bfb8805c poprzez ten adres +20- lub Chyba -20 - to wszystko z pamięci przeznaczonej na tej tablicy. Tablica, zmienna nie jest przechowywane w dowolnym miejscu. Kiedy jesteś kompilacji, kompilator - wave ręka na to - ale kompilator po prostu użyć, gdy wie, tablica jest. To, że nie wie, gdzie tablica zostanie uruchomiony, i tak zawsze może po prostu robić rzeczy w zakresie offsetu od tego początku. Nie potrzebujemy zmiennej samodzielnie reprezentować tablicę. Ale kiedy coś jak int * = p tablicy, teraz p jest wskaźnikiem, który wskazuje na tej tablicy a teraz p faktycznie istnieje na stosie. Jestem wolny, aby zmienić p. Mogę zrobić, p = malloc. Więc początkowo wskazał na tablicy, a teraz wskazuje do pewnego miejsca na stercie. Nie mogę Array = malloc. Jeśli Clang jest mądry, to krzycz na mnie tuż nietoperza. Faktycznie, jestem pewien, że gcc zrobi to zbyt. Więc typ array 'int [5] "nie jest przypisane. Nie można przypisać coś do typu tablicy ponieważ tablica jest po prostu stałą. Jest to symbol, który referencje te 20 bajtów. I nie można go zmienić. [Uczeń] A gdzie jest rozmiar tablicy przechowywane? [Bowden] To nie jest nigdzie zapisane. To kiedy jest kompilacją. Więc gdzie jest rozmiar tablicy przechowywane? Można używać tylko sizeof (array) wewnątrz funkcji, która tablica jest zadeklarował. Więc jeśli mogę zrobić kilka funkcji, Foo, i ja (int tablica []) printf ("% d \ n", sizeof (array)); a następnie w dół tu wywołać foo (tablica); wewnątrz tej funkcji - niech go uruchomić. To jest mądry Clang ponownie. To mówi mi, że parametr funkcji sizeof na tablicy zwróci rozmiar 'int *'. To byłby błąd, jeśli to nie jest to, co chciałem się stać. Miejmy faktycznie wyłączyć Werror. Ostrzeżenie. Ostrzeżenia są w porządku. Będzie nadal kompilacji ile ma ostrzeżenia. . / A.out będzie drukować 4. Ostrzeżenie, że został wygenerowany, wyraźnie wskazuje, co poszło źle. Ten int tablica jest po prostu będzie drukować sizeof (int *). Nawet jeśli mogę umieścić tablicę [5] tu, to jeszcze po prostu się do wydrukowania sizeof (int *). Więc jak najszybciej przekazać go do funkcji, rozróżnienie między tablicami i wskaźnikami nie istnieje. To dzieje się tablica, która została zadeklarowana na stos, ale jak tylko przekazać te wartości, które 0xbf bla, bla, bla do tej funkcji, wówczas wskaźnik wskazuje na tej tablicy na stosie. To znaczy, że sizeof zastosowanie tylko do funkcji, która została zadeklarowana tablica, co oznacza, że ​​podczas kompilacji z tej funkcji, kiedy Clang przechodzi tej funkcji, widzi tablicy jest int tablica wielkości 5. Więc widzi sizeof (tablica). Dobrze, że to 20. To właściwie jak sizeof zasadniczo działa w prawie wszystkich przypadkach. Sizeof nie jest funkcją, to jest operator. Nie nazywamy sizeof funkcję. Sizeof (int), kompilator będzie tylko tłumaczenie, że do 4. Masz? Okay. [Uczeń] Więc jaka jest różnica między sizeof (array) w głównym, w foo? To dlatego mówimy sizeof (tablica), co ma * typu int, natomiast macierz tu nie jest z typu int *, to int tablica. [Uczeń] Więc jeśli miał parametr w tablicy [] zamiast int tablicy *, by to oznaczało, że można jeszcze zmienić tablicę, bo teraz to jest wskaźnik? [Bowden] Podoba Ci się? >> [Uczeń] Tak. Można zmienić tablicę w funkcji teraz? [Bowden] Można zmienić tablicę w obu przypadkach. W obu tych przypadkach, jesteś wolny, aby powiedzieć, tablica [4] = 0. [Uczeń] Ale można zrobić punkt tablicy na coś innego? [Bowden] Oh. Tak. W każdym przypadku - >> [uczeń] Tak. [Bowden] rozróżnienie tablicy [] i int array *, nie ma. Możesz również zdobyć tablicę wielowymiarową tutaj dla niektórych wygodnej składni, ale to wciąż tylko wskaźnik. Oznacza to, że jestem wolny, aby zrobić tablicę = malloc (sizeof (int)), a teraz wskaż gdzie indziej. Ale tak jak jak to działa zawsze i na zawsze, Zmiana tej tablicy robiąc to wskazywać na coś innego nie zmienia tej tablicy w dół, bo to kopia argumentu nie wskaźnik do tego argumentu jest. I rzeczywiście, tak jak więcej wskazaniem, że jest to dokładnie to samo - już widziałem co wydruki tablicy drukowania - co, jeśli drukujemy adres tablicy lub adresu na adres tablicy do jednej z tych? Zignorujmy ten. Okay. To jest w porządku. Jest to teraz działa. / A.out. Array druku, drukowanie adres tablicy, to samo. Array po prostu nie istnieje. Ona wie, podczas drukowania tablicy, drukuje się symbol, który odnosi się do tych 20 bajtów. Drukowanie adres tablicy, dobrze, tablica nie istnieje. To nie ma adresu, więc to po prostu drukuje adres tych 20 bajtów. Tak szybko, jak skompilować w dół, podobnie jak w skompilowanego buggy4. / A.out, tablica nie istnieje. Wskaźniki istnieje. Tablice nie. Bloki pamięci reprezentujący tablicę nadal istnieją, ale zmienna tablicowa i zmienne tego typu nie istnieją. Są jak głównych różnic między tablicami i wskaźnikami są jak najszybciej dokonać wywołania funkcji, nie ma różnicy. Ale wewnątrz funkcji, która jest zadeklarowana sama tablica, sizeof działa inaczej drukuje się od wielkości bloków zamiast wielkości typu i nie można go zmienić, ponieważ jest to symbol. Drukowanie rzeczy i adres rzeczy wypisuje to samo. I to jest dość dużo. [Uczeń] Można powiedzieć, że jeszcze raz? I może coś przeoczyć. Array Drukowanie i adres tablicy wypisuje to samo, natomiast w przypadku drukowania wskaźnik stosunku adresem wskaźnika, jedno drukuje adres czego wskazując, Inne drukuje adresu wskaźnika na stosie. Można zmienić wskaźnik, nie można zmienić symbol tablicy. I wskaźnik sizeof będzie drukować wielkości tego typu wskaźnika. Więc int * p sizeof (p) będzie drukować 4, ale int tablica [5] print sizeof (array) będzie drukować 20. [Uczeń] Więc int tablica [5] będzie drukować 20? >> Tak. Dlatego wewnątrz buggy4 gdy kiedyś sizeof (tablica) to robi i <20, która nie jest, co chcieliśmy. Chcemy i <5. >> [Uczeń] Dobra. [Bowden], a następnie jak najszybciej rozpocząć przechodzącą w funkcji, gdybyśmy zrobili int * p = array; wewnątrz tej funkcji, możemy w zasadzie używać p oraz tablicę w dokładnie tych samych sposobów, oprócz problemu sizeof i problemu zmienia. Ale p [0] = 1; jest taka sama, jak mówi tablicy [0] = 1; I tak szybko, jak to mówimy foo (tablica) lub foo (p); wewnątrz foo funkcji, to jest dwa razy, to samo. Nie ma żadnej różnicy między tymi połączeniami. Wszyscy dobrze na tym? Okay. Mamy 10 minut. Postaramy się uzyskać w ramach tego programu Typer hakerów Ta strona, która ukazała się w zeszłym roku, czy coś. To jest po prostu być jak wpisać losowo i wypisze - Cokolwiek plik zdarza załadowany jest tym, co wygląda jak piszesz. To wygląda jak jakiś kod systemu operacyjnego. To, co chcemy realizować. Powinieneś mieć pliku wykonywalnego o nazwie hacker_typer że bierze w jednym argumentem, plik "typu hacker". Bieganie wykonywalny należy wyczyścić ekran a następnie wydrukować jeden znak z pliku przekazanego-w każdej chwili, gdy użytkownik naciśnie klawisz. Więc cokolwiek klawisz nacisnąć, należy wyrzucić i zamiast drukować znak z pliku to argument. Ja dość dużo powiedzieć, co te rzeczy Będziemy wiedzieć są. Ale chcemy, aby sprawdzić biblioteki termios. Nigdy nie używałem tej biblioteki w całym moim życiu, a więc ma bardzo minimalne cele. Ale to będzie biblioteka możemy użyć, aby wyrzucić charakter trafisz gdy piszesz do standardu w. Więc hacker_typer.c, a my będziemy chcieli # include. Patrząc na stronie podręcznika termios - jestem zgadywania w terminalu OS lub coś - I nie wiem, jak go odczytać. Patrząc na to, to mówi, aby objąć te 2 pliki, więc zrobimy to. Pierwszą rzeczą po pierwsze, chcemy podjąć w jednym argumentem, który plik należy otworzyć. Więc co chcę zrobić? Jak mogę sprawdzić, mam jeden argument? [Uczeń] Jeśli argc wynosi go. >> [Bowden] Tak. So if (argc = 2!) Printf ("Użycie:% s [file otworzyć]"). Więc teraz, jeśli to uruchomić bez podania argumentu - oh, potrzebuję nową linię - zobaczysz mówi usage:. / hacker_typer, a następnie drugi argument powinien być plik chcę otworzyć. Co mam teraz zrobić? Chcę, aby odczytać z tego pliku. Jak czytać z pliku? [Uczeń] go otworzyć pierwszy. >> Tak. Więc fopen. Co fopen wyglądać? [Uczeń] Filename. >> [Bowden] Filename będzie argv [1]. [Uczeń], a następnie to, co chcesz z tym zrobić, więc - >> [Bowden] Tak. Więc jeśli nie pamiętam, po prostu może zrobić fopen man, gdzie będzie const char * path, gdzie ścieżka jest nazwa pliku, const char * mode. Jeśli zdarzy się, nie pamiętam, co jest tryb, potem można szukać trybie. Wewnątrz stron podręcznika, znak ukośnika, co można użyć, aby szukać rzeczy. Więc wpisać / tryb wyszukiwania trybie. N i N są tym, co można wykorzystać, aby przejść przez mecze wyszukiwania. Tutaj mówi Argument mode wskazuje na łańcuch początku z jedną z następujących sekwencji. Więc r, Otwórz plik tekstowy do czytania. To, co chcemy zrobić. Do czytania, a ja chcę, aby zapisać, że. Rzeczą będzie plik *. Teraz to, co chcę zrobić? Daj mi sekundę. Okay. Teraz to, co chcę zrobić? [Student] Sprawdź, czy jest NULL. >> [Bowden] Tak. Za każdym razem, otworzyć plik, upewnij się, że jesteś z powodzeniem mógł go otworzyć. Teraz chcę zrobić to termios rzeczy tam, gdzie chcę, aby najpierw przeczytać moje aktualne ustawienia i uratować tych, w coś, to chcę, aby zmienić ustawienia wyrzucić dowolny znak i typ, a chcę zaktualizować te ustawienia. , A następnie na koniec programu, chcę wrócić do moich oryginalnych ustawień. Więc struct będzie z termios typu, i będę chciał dwa z nich. Pierwszy z nich będzie moje current_settings, i wtedy będziesz w moich hacker_settings. Po pierwsze, mam zamiar chcesz zapisać swoje bieżące ustawienia, potem zamierzam chcesz zaktualizować hacker_settings, a następnie drogą na końcu mojego programu, chcę wrócić do bieżących ustawień. Więc zapis aktualnych ustawień, sposób w jaki działa, mamy termios man. Widzimy, że mamy to, int int tcsetattr tcgetattr. I przechodzą w struct termios przez jego wskaźnik. Sposób to będzie wyglądać to - I've już zapomniał, co funkcja została wywołana. Skopiować i wkleić. Więc tcgetattr, to chcę przekazać w struct że jestem zapisywania informacji w, który będzie current_settings, i pierwszy argument jest deskryptor pliku dla rzeczy chcę zapisać atrybuty. Co deskryptor jest to jak każdym czasie otwierania pliku, pobiera deskryptor pliku. Kiedy fopen argv [1], to dostaje deskryptor pliku którego się odnosisz kiedy się chce czytać lub pisać. To nie deskryptor chcę użyć tutaj. Istnieją trzy deskryptory plikowe masz domyślnie, które są standardem, standard, i błąd standardowy. Domyślnie, myślę, że to standard w to 0, obecnie średnia to 1, błąd standardowy jest 2. Więc co chcę zmienić ustawienia? Chcę zmienić ustawienia, gdy uderzę znak, Chcę rzucić ten znak od zamiast wysyłać je na ekranie. Co stream - standard, standardowe wyjście lub błędu standardowego - reaguje na rzeczy, po wpisaniu na klawiaturze? >> [Uczeń] Standard widok >> Tak. Więc można albo zrobić 0 lub mogę stdin. Otrzymuję current_settings wzorca w. Teraz chcę, aby zaktualizować te ustawienia, więc najpierw ja skopiować do hacker_settings co moi current_settings są. A jak praca elemencie jest to po prostu skopiować. Spowoduje to skopiowanie wszystkich polach, jak można by oczekiwać. Teraz chcę zaktualizować niektóre pola. Patrząc na termios, trzeba by przeczytać wiele tego po prostu zobaczyć, co chcesz szukać, ale flagi Będziesz chcesz szukać to echo, tak ECHO znaków wejściowych Echo. Najpierw chcę ustawić - I've już zapomniał, co pola. To co struct wygląda. Więc tryby wprowadzania myślę, że chcemy zmienić. Przyjrzymy się w roztworze, aby upewnić się, że to, co chcemy zmienić. Chcemy zmienić lFlag w celu uniknięcia konieczności przejrzeć wszystkie te. Chcemy zmienić lokalne tryby. Trzeba by przeczytać cały ten rzeczy zrozumieć, gdzie wszystko należy że chcemy zmienić. Ale to jest w środku lokalnych trybach, gdzie mamy zamiar to zmienić. Więc hacker_settings.cc_lmode jest to, co się nazywa. c_lflag. To jest, gdy mamy do bitowe operatorów. Jesteśmy trochę z opóźnieniem, ale pojedziemy przez to bardzo szybko. To jest, gdy mamy do operatory bitowe, gdzie myślę, że jeden raz dawno temu, że przy każdym uruchomieniu czynienia z flagami, zamierzasz używać operatory bitowe dużo. Każdy bit odpowiada flaga jakiegoś zachowania. Więc tutaj, ta flaga ma kilka różnych rzeczy, gdzie wszystkie z nich oznacza coś innego. Ale to, co chcę zrobić, to wyłączyć bit odpowiadający ECHO. Tak, aby to wyłączyć zrobić & = ¬ ECHO. Właściwie myślę, że to jest jak techo czy coś. Idę sprawdzić ponownie. Mogę termios go. To tylko echo. ECHO będzie pojedynczy bit. ¬ ECHO będzie oznaczać wszystkie bity są ustawione na 1, co oznacza, że ​​wszystkie flagi są ustawione na true wyjątkiem bitu ECHO. Kończąc moje lokalnych flag z tym, to znaczy, wszystkie flagi, które są aktualnie ustawione na true wciąż będą ustawione na true. Jeśli moja flaga ECHO jest ustawiony na true, to jest koniecznie ustawić na false na flagę ECHO. Tak więc ta linia kodu po prostu wyłącza flagę ECHO. Pozostałe linie kodu, po prostu skopiuj je w interesie czasie, a następnie je wyjaśnić. W roztworze, mówi 0. To chyba lepiej, aby wyraźnie powiedzieć, stdin. Zauważ, że ja również robi echo | ICANON tutaj. ICANON odnosi się do czegoś, co oznacza oddzielną kanoniczny tryb. Co oznacza tryb kanoniczny zazwyczaj gdy piszesz z wiersza poleceń, standard nie przetwarza niczego aż trafisz linią. Więc kiedy się getString, wpisać kilka rzeczy, a potem trafisz linią. To jest, gdy jest wysłany do standardu w. To jest domyślna. Kiedy wyłączyć kanoniczny tryb, teraz każdy pojedynczy znak naciśnięciu jest to, co zostaje przetworzone, które jest zazwyczaj trochę zły, bo to wolno przetwarzać te rzeczy, dlatego dobrze jest go do bufora całych linii. Ale chcę, każdy znak do przetworzenia ponieważ nie ma to czekaj na mnie uderzyć newline przed przetwarza wszystkie znaki byłem pisania. To wyłącza kanoniczny tryb. Te rzeczy po prostu oznacza, podczas gdy w rzeczywistości przetwarza znaki. Oznacza to, przetwarzać je natychmiast, jak tylko piszę je, je przetwarzać. I jest to funkcja, która jest aktualizacją moje ustawienia do standardowych w, i środki TCSA zrobić to właśnie teraz. Inne opcje znajdują się czekać, aż wszystko, co jest obecnie w strumieniu jest przetwarzane. To naprawdę nie ma znaczenia. Wystarczy teraz zmienić moje ustawienia się, co jest obecnie w hacker_typer_settings. Chyba nazwał go hacker_settings, więc zmieńmy to. Zmień wszystko hacker_settings. Teraz na koniec naszego programu będziemy chcesz przywrócić tego, co jest obecnie wewnątrz normal_settings, który będzie po prostu wyglądać i normal_settings. Zauważ, że nie zmieniły się któryś z moich normal_settings od pierwotnie coraz to. Wtedy po prostu zmienić ich z powrotem, I przekazać je na końcu. To było aktualizacji. Okay. Teraz wewnątrz tutaj ja po prostu wyjaśnić kod w interesie czasie. To nie jest dużo kodu jest. Widzimy czytamy znak z pliku. Nazwaliśmy go f. Teraz możesz człowiek fgetc, ale jak fgetc zadziała jest po prostu to się zwracają znak, że po prostu czytać lub EOF, , co odpowiada w końcu lub jakiegoś happeningu pliku błędów. Jesteśmy zapętlenie, nadal odczytać pojedynczy znak z pliku, dopóki nie zabraknie znaków czytać. A gdy to robimy, że mamy czekać na pojedynczy znak ze standardowego w. Za każdym razem, wpiszesz coś w linii poleceń, że czyta się w postać z normą w. Następnie putchar właśnie zamiar umieścić char czytamy tutaj z pliku na wyjście standardowe. Możesz człowiek putchar, ale to tylko wprowadzenie do standardu obecnie, to drukowanie ten znak. Można też po prostu zrobić printf ("% c", c); sam pomysł. Że zrobi większość naszej pracy. Ostatnią rzeczą jaką będziesz chciał zrobić to właśnie fclose nasz plik. Jeśli nie fclose, to jest wyciek pamięci. Chcemy fclose plik pierwotnie otwarte i myślę, że to jest to. Jeśli zrobimy, że ja już mam problemy. Zobaczmy. Jakie to narzekać? Oczekiwano "int", ale argument jest typu "struct _IO_FILE * '. Zobaczymy, czy to działa. Dozwolone tylko w C99. Augh. Dobrze, aby hacker_typer. Teraz mamy więcej przydatnych opisów. Tak więc korzystać z nielegalnej identyfikator "normal_settings". I nie nazywać normal_settings. Nazwałem to current_settings. Więc zmieńmy wszystko. Teraz przechodząc argument. Zrobię to 0 do teraz. Okay. . / Hacker_typer cp.c. Też nie wyczyszczenie ekranu na początku. Ale można spojrzeć do ostatniego zestawu problemów, aby zobaczyć, w jaki sposób wyczyścić ekran. To tylko niektóre znaki drukowania podczas gdy to robi to, co chcę zrobić. Okay. I myśląc o tym, dlaczego to musiał być 0 zamiast standardowego wejścia, które należy # define 0, narzeka, że ​​jest to - Wcześniej, kiedy powiedziałem, że nie deskryptorów ale masz również swój plik *, deskryptor pliku jest tylko jedna liczba całkowita, natomiast * FILE ma całą masę rzeczy z nim związane. Powodem trzeba powiedzieć 0 zamiast z stdin jest to, że jest stdin * FILE, który wskazuje na tym co jest odsyłania deskryptor 0. Więc nawet tutaj, gdy zrobić fopen (argv [1], Dostaję * plik z powrotem. Ale gdzieś w tym FILE * jest rzeczą odpowiada deskryptor pliku dla tego pliku. Jeśli spojrzeć na stronie man otwarte, więc myślę, że trzeba zrobić człowiek 3 open - nope - man 2 open - tak. Jeśli spojrzeć na strony dla otwarty, jest jak fopen niższym poziomie i to wraca rzeczywistego deskryptora pliku. fopen robi kilka rzeczy na górze otwarte, który zamiast wrócić tylko, że deskryptor zwraca cały plik * wskaźnik wewnątrz której nasza mała deskryptor. Więc standard odnosi się do rzeczy * FILE, natomiast 0 oznacza tylko normy deskryptora pliku w sobie. Questions? [Śmiech] Blew przez to. Dobrze. Skończyliśmy. [Śmieje się] [CS50.TV]