[Powered by Google Translate] [CS50 Library]
[Nate Hardison] [Harvard University]
[To jest CS50. CS50.TV]
Biblioteka CS50 to przydatne narzędzie, które zostało zainstalowane na urządzeniu
aby ułatwić do pisania programów, które monituje użytkowników do wejścia.
W tym filmie, będziemy odciągnąć zasłonę i spójrz na to, co dokładnie jest w CS50 biblioteki.
>> W wideo na bibliotekach C, mówimy o tym, jak nagłówki plików # include
biblioteki w kodzie źródłowym,
a następnie połączyć się z pliku binarnego biblioteki podczas fazy łączącej
procesu kompilacji.
Pliki nagłówkowe określić interfejs biblioteki.
Oznacza to, że informacje o wszystkich zasobów, że biblioteka ma dostępne do użycia,
jak deklaracji funkcji, stałych i typów danych.
Plik binarny biblioteki zawiera implementację biblioteki
który jest skompilowany z biblioteki na pliki nagłówkowe i biblioteki pliki. c kod źródłowy.
>> Plik binarny biblioteka nie jest bardzo ciekawe spojrzenie na, ponieważ jest dobrze, w binarnym.
Więc, rzućmy okiem na pliki nagłówkowe dla biblioteki zamiast.
W tym przypadku jest tylko jeden plik o nazwie header cs50.h.
Mamy zainstalowany w katalogu użytkownika, to
wraz z plikami innych bibliotek systemu "nagłówka.
>> Jedną z pierwszych rzeczy, można zauważyć, jest to, że cs50.h # zawiera pliki nagłówkowe z innych bibliotek -
float, bool limity standardowe i standardowe lib.
Ponownie, zgodnie z zasadą nie wymyślać koła,
mamy zbudował CS0 bibliotekę przy użyciu narzędzi, które inne przewidziane dla nas.
>> Następną rzeczą, którą zobaczysz w bibliotece jest to, że możemy zdefiniować nowy typ o nazwie "string".
Ta linia tak naprawdę tworzy alias dla typu char *,
więc nie magicznie nadać nowy typ ciąg z atrybutami
często związane z obiektami smyczkowych w innych językach,
, takie jak długość.
Powodem zrobiliśmy to, aby chronić nowe programistów od małych detali
wskaźników, dopóki nie jesteśmy gotowi.
>> Kolejna część nagłówka pliku jest deklaracja funkcji
że CS50 biblioteki zapewnia wraz z dokumentacją.
Wskazówka poziomu szczegółowości w komentarzach tutaj.
To jest super ważne, tak, że ludzie wiedzą, jak korzystać z tych funkcji.
Deklarujemy z kolei działa, aby skłonić użytkownika i znaków powrotu, Gra Podwójna, pływaków, wskazówki,
długo tęskni i łańcuchy, za pomocą własnego typu string.
Zgodnie z zasadą ukrywania informacji,
musimy umieścić naszą definicję w oddzielnym pliku c realizacji -. cs50.c--
znajduje się w katalogu źródłowym użytkownika.
Mamy pod warunkiem, że plik, tak, że można spojrzeć na nią,
uczyć się od niego, i skompilować go na różnych komputerach, jeśli chcesz,
chociaż myślę, że to lepiej pracować na urządzenie w tej klasie.
Anyway, rzućmy okiem na to teraz.
>> Funkcje getchar, GetDouble, GetFloat, getInt i GetLongLong
są zbudowane na górze GetString funkcji.
Okazuje się, że oni wszyscy idą zasadniczo ten sam wzór.
Używają pętlę while, aby skłonić użytkownika do jednej z linii wejściowych.
Zwracają szczególną wartość, jeśli użytkownik wejścia pustą linię.
Próbują analizować wprowadzane przez użytkownika jako odpowiedniego typu,
czy to char, double, float, itp.
A potem albo zwraca wynik, jeśli wejście z powodzeniem analizowane
lub ich reprompt użytkownika.
>> Na wysokim poziomie, nie ma nic naprawdę trudne tutaj.
Możesz napisać o podobnej strukturze kodu się w przeszłości.
Być może najbardziej tajemniczy przystojny część jest sscanf wezwanie który analizuje wprowadzane przez użytkownika.
Sscanf jest częścią formatu wejściowego rodziny konwersji.
Zamieszkuje io.h standardowej, a jego zadaniem jest analizować ciąg C,
zgodnie z określonym formatem, przechowywania wyników analizować przy zmiennej
dostarczone przez dzwoniącego.
Ponieważ funkcje format wejściowy konwersji są bardzo przydatne, powszechnie używane funkcje
że nie są super intuicyjny w pierwszym,
pójdziemy nad tym, jak sscanf działa.
>> Pierwszy argument sscanf jest char * - wskaźnik do znaku.
Aby funkcja działała poprawnie,
że znak powinien być pierwszym znakiem łańcucha C,
zakończony null \ 0 znak.
Jest to ciąg do analizowania
Drugi argument sscanf jest ciąg formatu,
zazwyczaj przekazywana jako stałej strun
a może widzieliście ciąg jak to wcześniej przy użyciu printf.
Znak procent w ciągu formatu wskazuje specyfikacją konwersji.
Charakter bezpośrednio po znaku procentu,
wskazuje typ C, że chcemy sscanf przekonwertować.
W getInt, widać, że jest d% c% i.
Oznacza to, że będzie starał się sscanf dziesiętna int -% d - i char - c%.
Dla każdego specyfikatora konwersji w ciągu formatu,
sscanf oczekuje odpowiadający mu argument później w liście argumentów.
Argument ten musi wskazywać na miejscu odpowiednio wpisywanych
, w którym w celu przechowywania wyniku konwersji.
>> Typowym sposobem na to jest do utworzenia zmiennej na stosie przed wywołaniem sscanf
dla każdej pozycji, którą chcesz analizować z łańcucha
a następnie użyć operatora adres - ampersand - aby przekazać wskaźniki
tych zmiennych na wezwanie sscanf.
Widać, że w getInt robimy dokładnie to.
Tuż przed zaproszenia sscanf oświadczamy, int o nazwie n i char c połączenia na stosie,
i mijamy wskaźniki do nich w zaproszeniu sscanf.
Umieszczenie tych zmiennych na stosie jest preferowana przy użyciu przestrzeni dyskowej
na stercie z malloc, ponieważ uniknie się zaproszenia malloc,
i nie trzeba się martwić o wycieki pamięci.
Postacie nie poprzedzone znakiem procentu nie poprosi konwersji.
Raczej po prostu dodać do specyfikacji formatu.
>> Na przykład, jeżeli w ciągu formatu getInt były d% zamiast
sscanf wyglądałaby dla litery a następnie int,
i chociaż to spróbuje przekonwertować int, nie byłoby nic innego z A.
Jedynym wyjątkiem jest to spacja.
Białe znaki w ciąg formatu dopasować dowolną ilość spacji -
nawet wcale.
Więc dlatego comment wspomina ewentualnie prowadzi i / lub końcowe białe znaki.
Tak, w tym momencie wygląda jak nasz apel sscanf będzie próbował analizować przez użytkownika ciąg znaków
poprzez sprawdzenie możliwości wiodącego spacją,
następnie int będzie skonwertowany i przechowywane w zmiennej n int
następnie pewnych ilości spacji, a następnie znak
przechowywane w char c zmiennej.
>> Co o wartości powrotnej?
Sscanf będzie analizować linię wejściową od początku do końca,
zatrzymania, gdy dojdzie do końca, gdy znak lub na wejściu
nie pasuje znak formatu lub gdy nie może dokonać konwersji.
To zakończenia używany jest wyodrębnić, gdy przestał.
Jeśli się zatrzymał, ponieważ osiągnięty koniec ciągu wejściowego
przed dokonaniem konwersji, a przed nie pasujące części łańcucha formatu,
Następnie specjalna stała EOF jest zwracana.
W przeciwnym razie zwraca liczbę udanych konwersji,
które może wynosić 0, 1, lub 2, od Poprosiliśmy dwóch konwersji.
W naszym przypadku, chcemy się upewnić, że użytkownik wpisze w int i tylko int.
>> Tak, chcemy sscanf wrócić 1. Zobacz, dlaczego?
Jeśli sscanf zwróciło 0, wtedy nie konwersje zostały dokonane,
więc użytkownik wpisze coś innego niż int na początku wejścia.
Jeśli sscanf zwraca 2, użytkownik nie prawidłowo wpisać w na początku wejścia
ale następnie wpisane w jakiś nie-białym znakiem potem
od% c konwersji udało.
Wow, to całkiem długie wyjaśnienie dla jednego wywołania funkcji.
W każdym razie, jeśli chcesz więcej informacji na temat sscanf i jej rodzeństwo,
zobacz strony podręcznika, Google, lub obu.
Istnieje wiele opcji, format string,
a te mogą zaoszczędzić dużo pracy ręcznej, gdy próbuje analizować ciągi w C.
>> Ostatnią funkcją w bibliotece patrzeć na to GetString.
Okazuje się, że jest to trudne GetString funkcja pisać poprawnie,
choć wydaje się, że taki prosty, zadania wspólne.
Dlaczego jest to przypadek?
No cóż, myślę o tym, jak będziemy przechowywać wiersz użytkownik wpisze w.
Ponieważ ciąg jest ciągiem znaków,
możemy chcieć przechowywać w tablicy na stosie,
ale trzeba wiedzieć, jak długo tablica będzie, kiedy to zgłosić.
Podobnie, jeśli chcemy, aby umieścić go na stercie,
musimy przekazać do malloc liczbę bajtów chcemy rezerwy,
, ale nie jest to możliwe.
Nie mamy pojęcia, jak wiele znaków użytkownik wpisać
zanim użytkownik faktycznie musi je wpisać.
>> Naiwne rozwiązanie tego problemu jest po prostu zarezerwować spory kawałek przestrzeni, powiedzmy,
Blok 1000 znaków dla użytkownika danych wejściowych,
przy założeniu, że użytkownik nigdy nie wpisywać w ciąg tak długo.
To jest zły pomysł z dwóch powodów.
Po pierwsze, przy założeniu, że użytkownicy zazwyczaj nie wpisywać ciągi znaków, które długo
można tracić pamięć.
Na nowoczesnych maszynach, to może nie być problemem, jeśli to zrobisz
w jednym lub dwóch pojedynczych przypadkach
ale jeśli bierzesz użytkownika dane w pętli i przechowywania w celu późniejszego wykorzystania,
można szybko wysysają mnóstwo pamięci.
Dodatkowo, jeśli program piszesz jest dla mniejszej komputera -
urządzenie takie jak smartphone czy coś innego z ograniczoną pamięcią -
rozwiązanie to będzie powodować problemy znacznie szybciej.
Drugi, bardziej poważny powód, aby nie jest to, że pozostawia program podatny
co się nazywa atak przepełnienia bufora.
W programowaniu, bufor jest pamięć używana do tymczasowego przechowywania danych wejściowych lub wyjściowych,
która w tym przypadku jest nasz char blok 1000.
Przepełnienie bufora, gdy dane są zapisywane poza końcem bloku.
>> Na przykład, jeśli użytkownik ma typ faktycznie w ponad 1000 znaków.
Być może doświadczyliśmy tego przypadkowo podczas programowania z tablicami.
Jeśli masz tablicę 10 liczb całkowitych, nic nie zatrzyma cię z próby odczytu lub zapisu
15. int.
Brak ostrzeżeń kompilatora lub błędy.
Program tylko gafy prosto i uzyskuje dostęp do pamięci
gdzie myśli 15. int będzie, a to może nadpisać inne zmienne.
W najgorszym przypadku można zastąpić część swojego programu wewnętrznego
mechanizmów kontroli, powodując program rzeczywiście wykonać różne instrukcje
niż zamierzony.
>> Teraz to nie jest wspólne, aby to zrobić przypadkowo,
, ale jest to dość powszechna technika źli używać złamać programy
i umieścić złośliwy kod na komputerach innych osób.
Dlatego nie możemy po prostu skorzystać z naszego naiwnego rozwiązania.
Musimy znaleźć sposób, aby zapobiec nasze programy będąc podatne
na atak przepełnienia bufora.
Aby to zrobić, musimy się upewnić, że nasz bufor może rosnąć jak czytamy
więcej wejście od użytkownika.
Rozwiązanie? Używamy bufor przydzielonej sterty.
Ponieważ możemy zmienić jego rozmiar za pomocą funkcji Resize realloc,
i śledzić z dwóch numerów - indeks następnego wolnego gniazda w buforze
oraz długość i ilość bufora.
Czytamy w znaki od użytkownika po jednym na raz używając fgetc funkcję.
Argument fgetc funkcja przyjmuje - stdin - to odniesienie do standardowego ciągu wejściowego,
który jest preconnected kanał wejściowy, który jest używany do przesyłania wprowadzane przez użytkownika
z terminala do programu.
>> Ilekroć użytkownik wpisze w nowym charakterze, możemy sprawdzić, czy wskaźnik
następnego wolnego otworu oraz 1 jest większa niż pojemność bufora.
+1 Przychodzi bo jeśli następny wolny indeks jest 5,
Następnie nasz bufora długość musi być 6 dzięki 0 indeksowania.
Jeśli zabrakło miejsca w buforze, a następnie staramy się zmienić jego rozmiar,
podwojenie go tak, że obniżyć liczbę razy, że rozmiar
jeśli użytkownik wpisując ciąg naprawdę długo.
Jeśli łańcuch ma zdobyć zbyt długo lub gdy zabraknie pamięci sterty,
Uwolnijmy nasze bufor i zwróć NULL.
>> Wreszcie, dołączyć char do bufora.
Gdy uderza użytkownika Enter lub Return, sygnalizując nowy wiersz,
lub specjalny znak - kontrola d - co sygnalizuje koniec danych wejściowych,
robimy sprawdzić, czy użytkownik faktycznie wpisane w cokolwiek.
Jeśli nie, to zwróci null.
Inaczej, ponieważ nasz bufor jest prawdopodobnie większy niż nam potrzeba,
w najgorszym przypadku jest to prawie dwa razy większy niż potrzebujemy
ponieważ dwukrotnie za każdym razem, zmiana rozmiaru,
tworzymy nową kopię napisu przy użyciu tylko ilość miejsca, że ​​musimy.
Możemy dodać dodatkowy 1 do zaproszenia malloc,
tak, że nie ma miejsca na szczególnym charakterze terminatora null - \ 0,
które dodajemy do ciągu, gdy będziemy kopiować w pozostałych bohaterów,
używając strncpy zamiast strcpy
tak, że możemy dokładnie określić, ile znaków chcemy skopiować.
Strcpy kopiuje aż uderza \ 0.
Potem uwolnić naszą bufora i zwraca kopię do rozmówcy.
>> Kto wiedział takie proste pozorna funkcja może być tak skomplikowane?
Teraz wiesz, co idzie do CS50 biblioteki.
>> Nazywam się Nate Hardison i to CS50.
[CS50.TV]