[Powered by Google Translate] [Sekcja 4 - bardziej komfortowe]
[Rob Bowden - Harvard University]
[To jest CS50. - CS50.TV]
Mamy jutro test, w przypadku wy nie wiedziałem.
Jest to w zasadzie na wszystko, co widzieliście w klasie lub powinien zobaczyć w klasie.
, Który zawiera wskaźniki, chociaż jest to bardzo niedawno temat.
Należy przynajmniej zrozumieć wysoki poziom nich.
Wszystko, co nie było na klasy należy zrozumieć na quiz.
Tak więc, jeśli masz pytania na ich temat, możesz zadać je teraz.
Ale to będzie bardzo student-led sesja gdzie wam zadawać pytania,
więc mam nadzieję, że ludzie mają pytania.
Czy ktoś ma pytania?
Tak. >> [Uczeń] można przejść wskaźniki ponownie?
Pójdę na wskaźniki.
Wszystkich swoich zmiennych muszą żyć w pamięci,
ale zazwyczaj nie martw się o tym i po prostu powiedzieć, x + 2 i y + 3
i kompilator będzie dowiedzieć się, gdzie te rzeczy żyją dla Ciebie.
Gdy masz do czynienia z wskaźników, teraz jesteś jawnie przy użyciu tych adresów pamięci.
Tak więc pojedyncza zmienna będzie zawsze tylko żyć pod jednym adresem w danej chwili.
Jeśli chcemy zadeklarować wskaźnik, co jest typ będzie wyglądać?
>> Chcę zadeklarować p wskaźnika. Co typ wygląda?
[Uczeń] int * p. >> Tak. Więc int * p.
A jak zrobić, żeby ją wskazać x? >> [Uczeń] Ampersand.
[Bowden] Więc ampersand dosłownie nazywane adres operatora.
Więc kiedy mówię & x robi się adres pamięci zmiennej x.
Więc teraz mam t wskaźnik, i nigdzie w moim kodu można używać * P
czy mogę używać X i będzie to dokładnie to samo.
(* P). Co to robi? Co oznacza, że ​​gwiazdka oznacza?
[Uczeń] To oznacza wartość w tym punkcie. >> Tak.
Więc jeśli spojrzymy na to, to może być bardzo przydatne, aby wyciągnąć schematy
tam gdzie jest to małe pudełko pamięci dla x, co zdarza się mieć wartość 4,
wtedy mamy małe okienko pamięci dla P,
i tak punktów p. X, więc narysować strzałkę od P do x.
Więc, gdy mówimy, p * mówimy przejść do pola, które jest p.
Star jest śledzić strzałkę i robić, co chcesz z tym polu tam.
Więc mogę powiedzieć, * p = 7, i że trafi do pudełka, które jest x, a zmiany, które do 7.
Albo można powiedzieć int z = * p * 2; To się myli, bo to gwiazda, gwiazda.
Jedna gwiazda jest dereferencji t, druga gwiazda jest pomnożenie przez 2.
Zauważ, mógłbym równie dobrze zastąpiła P * z x.
Można ich używać w taki sam sposób.
A później na I może mieć punkt P do zupełnie nowej rzeczy.
Mogę tylko powiedzieć, p = &z;
Więc teraz P nie wskazuje już na X, to wskazuje na z.
I za każdym razem zrobić P * to jest to samo, jak robi z..
Więc przydatna rzeczą jest to, kiedy już zaczną się do funkcji.
>> To trochę bezużyteczne zadeklarować wskaźnik, który wskazuje na coś
a potem po prostu go dereferencji
kiedy może użyłeś oryginalnej zmiennej na początku.
Ale kiedy się do funkcji - tak powiedzmy mamy kilka funkcji, int foo
która pobiera wskaźnik i po prostu robi * p = 6;
Jak widzieliśmy wcześniej z wymiany, nie można zrobić skuteczną swapa i oddzielną funkcję
by tylko przejazdem liczby całkowite, ponieważ wszystko w C zawsze przechodzi przez wartość.
Nawet jeśli jesteś przejazdem wskaźniki jesteś przejazdem przez wartość.
Tak się składa, że ​​te wartości są adresy pamięci.
Więc kiedy mówię foo (p); olewam wskaźnik do funkcji foo
a następnie foo robi * p = 6;
Tak więc w środku tej funkcji, * p nadal odpowiada X
, ale nie można używać X wewnątrz tej funkcji, ponieważ nie jest objęty zakresem wewnątrz tej funkcji.
Więc * p = 6 jest to jedyny sposób można uzyskać dostęp do lokalnej zmiennej z innej funkcji.
Albo dobrze, wskaźniki są jedynym sposobem, można uzyskać dostęp do lokalnej zmiennej z innej funkcji.
[Uczeń] Powiedzmy, że chciał wrócić wskaźnik. Jak dokładnie to zrobić?
[Bowden] zwraca wskaźnik jak w coś int y = 3; return & y? >> [Uczeń] Tak.
[Bowden] Dobra. Nigdy nie należy tego robić. To jest złe.
Myślę, że widziałem w tych slajdach wykładów początek widząc ten cały schemat pamięci
gdzie tu masz adres pamięci 0
a tu masz adres w pamięci 4 koncertów lub 2 do 32.
Więc masz trochę rzeczy i pewne rzeczy, a potem masz stos
i masz swoją kupę, która właśnie rozpoczęła naukę o, dorastanie.
[Uczeń] nie kupie powyżej stosu?
>> Tak. Kupa jest na górze, prawda? >> [Uczeń] Cóż, położył 0 na górze.
[Uczeń] Oh, położył 0 na górze. >> [Uczeń] Oh, w porządku.
Zastrzeżenie: Anywhere z CS50 masz zamiar zobaczyć to w ten sposób. >> [Uczeń] Dobra.
Tyle, że kiedy jesteś 1-ci widząc stosy,
jak wtedy, gdy uważasz, że stos myśleć układanie rzeczy na jeden na drugim.
Więc staramy się przerzucić to około tak stos rośnie jak stos normalnie
zamiast stosu zwisa. >> [Uczeń] nie hałdy technicznie dorastają zbyt, chociaż?
To zależy co masz na myśli przez dorosnąć.
Stosu i sterty zawsze rosną w przeciwnych kierunkach.
Stos jest zawsze dorastanie w tym sensie, że dorasta
w kierunku wyższych adresów pamięci i sterty rośnie w dół
w tym, że w kierunku niższych rośnie adresów pamięci.
Najlepiej jest więc 0 i dolny adresy pamięci jest wysokie.
Oboje rośnie, tylko w przeciwnych kierunkach.
[Uczeń] Chodziło mi o to, że ponieważ pan powiedział umieścić stos na dnie
, ponieważ wydaje się, ze względu na bardziej intuicyjne stosu zacząć od góry stos,
kupa jest na górze sobie też, więc that's - >> Tak.
Również pomyśleć o stercie jak dorasta i większe, ale stos bardziej.
Więc stos jest jeden, że niby chcą pokazać dorastanie.
Ale wszędzie wyglądasz inaczej pokaże adres 0 na górze
a najwyższy adres pamięci na dole, więc jest to zwykły widok z pamięci.
>> Czy masz pytanie?
[Student] Czy możesz nam powiedzieć więcej o stercie?
Tak. I ci się, że w drugim.
Po pierwsze, dlaczego wraca do powrotu & Y jest złe,
na stosie masz kilka ramek stosu, które reprezentują wszystkie funkcje
które nazywane.
Więc ignorując wcześniejsze rzeczy, w górnej części stosu jest zawsze będzie głównym zadaniem
ponieważ jest to pierwsza funkcja, która jest wezwaniem.
A potem, kiedy wywołać inną funkcję, stos ma rosnąć w dół.
Więc jeśli zgłoszę niektórych funkcji, Foo, i robi własne ramki stosu,
może wywołać jakąś funkcję, bar; robi własne ramki stosu.
I bar może być rekurencyjna i może wywoła się
i tak, że drugie zaproszenie do baru dostanie swoją ramkę stosu.
A więc to, co dzieje się w tych ramkach stosu są wszystkie zmienne lokalne
i wszystkie argumenty funkcji, która -
Wszelkie rzeczy, które są lokalnie zawężona do tej funkcji przejść w tych ramek stosu.
To znaczy, że kiedy powiedział coś jak bar jest funkcją,
Idę tylko do deklarowania liczby całkowitej, a następnie zwraca wskaźnik do tego całkowitej.
Więc skąd y mieszka?
[Uczeń] y mieszka w barze. >> [Bowden] Tak.
Gdzieś w tym małym placu w pamięci to, że ma kwadratowy Littler y w nim.
Kiedy wrócę & y, jestem zwracając wskaźnik do tego małego bloku pamięci.
Ale wtedy, gdy a funkcja zwraca jego ramki stosu pobiera zdejmowana stos.
I dlatego nazywa się to stosu.
To jak struktury danych stosu, jeśli wiesz, co to jest.
Albo nawet jak stos tac jest zawsze przykładem,
Głównym będzie iść na dno, to pierwsza funkcja zadzwonić będzie przejść na początku, że
i nie możesz wrócić do głównego, aż wrócisz z wszystkich funkcji, które zostały nazwane
, które zostały umieszczone na nim.
>> [Uczeń] Więc jeśli zrobił zwrot & Y, że wartość jest ulec zmianie bez powiadomienia.
Tak, it's - >> [uczeń] To może być zastąpiony. >> Tak.
Jest całkowicie - Jeśli spróbujesz -
Byłoby to również int bar * bo to zwracając wskaźnik,
więc jego powrót jest typ int *.
Jeśli spróbujesz użyć zwracanej wartości tej funkcji, jest niezdefiniowane zachowanie
dlatego, że wskaźnik wskazuje na złą pamięć. >> [Uczeń] Dobra.
Więc co, jeśli, na przykład, oświadczył int * y = malloc (sizeof (int))?
To jest lepsze. Tak.
[Uczeń] Rozmawialiśmy o tym, jak, kiedy przeciągnąć rzeczy do naszego kosza
nie są one rzeczywiście skasowane, po prostu tracą swoje wskaźniki.
Więc w tym przypadku mamy faktycznie usunąć wartość, czy jest to nadal istnieje w pamięci?
W przeważającej części, to będzie nadal istnieje.
Ale powiedzmy, że zdarzy nam się wywołać jakąś inną funkcję, baz.
Baz dostanie swoją ramkę stosu tutaj.
To będzie nadpisywania wszystkich tych rzeczy,
a jeśli później spróbować skorzystać wskaźnik, że masz przed,
to nie będzie ta sama wartość.
To się zmieniło tylko dlatego, że nazywa się BAZ funkcji.
[Uczeń] Ale gdyby nie my, będziemy nadal się 3?
[Bowden] Według wszelkiego prawdopodobieństwa, że ​​tak.
Ale nie można powoływać się na to. C po prostu mówi niezdefiniowane zachowanie.
>> [Uczeń] Oh, to nie. Okay.
Więc gdy chcesz powrócić wskaźnik, to jest, gdy malloc jest w użyciu.
Piszę właściwie tylko powrócić malloc (3 * sizeof (int)).
Pójdziemy na malloc więcej w drugim, ale idea jest malloc wszystkich zmiennych lokalnych
zawsze idą na stos.
Wszystko, co jest malloced idzie na stercie, i będzie na zawsze i być zawsze na stercie
dopóki jawnie zwolnić go.
Więc to oznacza, że ​​gdy malloc coś, to będzie przeżyć po powrocie funkcji.
[Student] Czy przetrwa po program zatrzyma? No >>
Okay, więc to będzie tam, aż program wszystko jest sposób zrobić uruchomiony. >> Tak.
Możemy iść na szczegóły tego, co się dzieje, gdy program przestanie działać.
Być może trzeba mi przypominać, ale to osobna sprawa całkowicie.
[Uczeń] Więc malloc tworzy wskaźnik? >> Tak.
Malloc - >> [uczeń] Myślę malloc oznacza blok pamięci, że wskaźnik może wykorzystać.
[Bowden] Chcę, aby ten schemat ponownie. >> [Uczeń] Więc ta funkcja działa, chociaż?
[Uczeń] Tak, malloc oznacza blok pamięci, których można używać,
, a następnie powraca na adres pierwszego bloku tej pamięci.
>> [Bowden] Tak. Więc gdy malloc, jesteś chwytając jakiś blok pamięci
to jest obecnie w stercie.
Jeżeli stos jest zbyt mała, a następnie po prostu sterty rosnąć i rośnie w tym kierunku.
Powiedzmy więc, że kupa jest zbyt mała.
Wtedy to właśnie rosną trochę i zwraca wskaźnik do tego bloku, które po prostu rósł.
Po darmowe rzeczy, robisz więcej miejsca w kupie,
tak to później zadzwonić do malloc może ponownie tej pamięci, które były wcześniej zwolnione.
Ważną rzeczą malloc i free jest to, że daje pełną kontrolę
w okresie eksploatacji tych bloków pamięci.
Zmienne globalne są zawsze żywe.
Zmienne lokalne są żywe w ich zakres.
Tak szybko, jak przejść obok klamra kręcone, zmienne lokalne są martwe.
Malloced pamięć jest żywa, gdy chcesz, aby żyć
a następnie jest uwalniany, gdy powiesz to do wydania.
Są to faktycznie tylko 3 rodzaje pamięci, naprawdę.
Jest automatyczne zarządzanie pamięcią, który jest stos.
Wszystko dzieje się automatycznie.
Kiedy mówisz, int x, pamięć została przydzielona do int x.
Gdy x wychodzi z zakresu, pamięć jest przeznaczane do x.
Potem jest dynamiczne zarządzanie pamięcią, która jest, co malloc jest
co jest, gdy masz kontrolę.
Ty decydujesz, kiedy dynamicznie pamięć powinna i nie powinna być przypisana.
A wtedy nie statyczne, co oznacza po prostu, że żyje wiecznie,
czyli to, co zmienne globalne są.
Oni po prostu zawsze w pamięci.
>> Pytania?
[Student] Czy możesz zdefiniować blok tylko za pomocą nawiasów klamrowych
ale nie trzeba mieć, jeśli oświadczenia lub while lub coś podobnego?
Można zdefiniować blok, jak w funkcji, ale ma nawiasy klamrowe też.
[Uczeń] Więc nie możesz po prostu mieć jak losowej nawiasach klamrowych w kodzie
które mają zmienne lokalne? >> Tak, można.
Wewnątrz int barze mogliśmy {int y = 3;}.
To ma być właśnie tutaj.
Ale to całkowicie definiuje zakres int y.
Po tym drugim nawiasem klamrowym, y nie może być już używany.
Ty prawie nigdy nie zrobić, choć.
Wracając do tego, co się dzieje, gdy program się kończy,
tam trochę nieporozumienie / pół kłamstwo, że dajemy, aby po prostu zrobić łatwiej.
Mówimy wam, że kiedy przydzielić pamięci
jesteś przydzielania którąś pamięci RAM dla tej zmiennej.
Ale ty nie jesteś naprawdę bezpośrednio dotykając RAM nigdy w swoich programach.
Jeśli myślisz o tym, jak zwróciłem -
I rzeczywiście, jeśli przejdziesz przez w GDB zobaczysz to samo.
Niezależnie od tego, ile razy można uruchomić program lub jakiego programu używasz,
stosu zawsze będzie start -
zawsze masz zamiar zobaczyć zmiennych wokół czegoś oxbffff adresowej.
To zazwyczaj gdzieś w tym regionie.
Ale jak 2 programy ewentualnie mieć wskaźniki do tej samej pamięci?
[Uczeń] Jest trochę arbitralne wyznaczenie gdzie oxbfff ma być w pamięci RAM
że w rzeczywistości może być w różnych miejscach, w zależności od, gdy funkcja została wywołana.
Tak. Termin jest pamięć wirtualna.
Chodzi o to, że każdy proces, każdy program uruchomiony na komputerze
ma swój własny - załóżmy 32 bitów - całkowicie niezależne przestrzeni adresowej.
Jest to przestrzeń adresowa.
Ma własne całkowicie niezależne 4 GB do wykorzystania.
>> Więc jeśli uruchomić 2 programów jednocześnie, program ten widzi 4 GB do siebie,
program ten widzi 4 GB do siebie,
i nie da się do tego programu do nieprawidłowego wskaźnika
i kończy się z pamięci z tego programu.
A co z pamięci wirtualnej jest odwzorowanie z przestrzeni adresowej procesów
do rzeczywistych rzeczy na RAM.
Więc to zależy od systemu operacyjnego, aby wiedzieć, że
hej, kiedy ten facet wskaźnik dereferences oxbfff, że naprawdę oznacza
że chce RAM bajt 1000,
natomiast jeśli to oxbfff dereferences program, naprawdę chce RAM bajt 10000.
Mogą być dowolnie daleko od siebie.
Jest to prawdziwe nawet w rzeczy jednej przestrzeni adresowej procesów.
Tak jak to widzi wszystkie 4 gigabajtów do siebie, ale powiedzmy -
[Student] Czy każdy proces -
Powiedzmy, że masz komputer z tylko 4 GB pamięci RAM.
Czy każdy pojedynczy proces zobaczyć całe 4 GB? >> Tak.
Ale 4 gigabajty to widzi, to kłamstwo.
To jest po prostu myśli, że to wszystko ma pamięć, bo nie wiem, żadna inna metoda nie istnieje.
Będzie używać tylko tyle pamięci, jak to faktycznie potrzebuje.
System operacyjny nie da się tego procesu RAM
Jeżeli to nie jest przy tym w żadnej pamięci całego regionu.
To się nie dać mu w pamięć dla tego regionu.
Ale idea jest taka, że ​​- Staram się myśleć o - Nie mogę myśleć o analogii.
Analogie są trudne.
Jednym z problemów, pamięci wirtualnej lub jedna z rzeczy, to jest rozwiązywanie
jest to, że proces powinien być całkowicie nieświadomy siebie.
I tak można napisać dowolny program, że tylko dereferences żadnego wskaźnika,
jak tylko napisać program, który mówi * (ox1234)
i to dereferencji adres pamięci 1234.
>> Ale to zależy od systemu operacyjnego, aby następnie w tłumaczeniu, co 1234 znaczy.
Więc jeśli 1234 dzieje się, że podany adres pamięci w tym procesie,
jak to jest na stosie, czy coś, to zwróci wartość tego adresu pamięci
ile proces wie.
Ale jeśli nie 1234, że podany adres jest, jak to się dzieje na ziemi
w pewnym niewielkim kawałkiem pamięci tutaj jest poza stos i poza stos
i naprawdę nie używane, że to jest to, gdy pojawi się takie rzeczy jak segfaultów
bo jesteś dotykając pamięć, że nie należy dotykać.
Odnosi się to również -
32-bitowy system, 32 bitów oznacza, że ​​masz 32 bitów do zdefiniowania adresu pamięci.
To dlatego wskaźniki są 8 bajtów, bo 32 bity to 8 bajtów - lub 4 bajty.
Wskaźniki są 4 bajty.
Więc kiedy widzisz wskaźnik jak oxbfffff, czyli -
W ramach danego programu można po prostu zbudować dowolną wskaźnik,
wszędzie od ox0 do wołu 8 f's - FFFFFFFF.
[Uczeń] nie można powiedzieć, że są 4 bajty? >> Tak.
[Uczeń] Następnie każdy bajt będzie - >> [Bowden] szesnastkowa.
Szesnastkowym - 5, 6, 7, 8. Więc wskaźników będziesz zawsze zobaczyć w systemie szesnastkowym.
To jest po prostu, jak klasyfikujemy wskaźniki.
Co 2 cyfry szesnastkowej jest 1 bajt.
Więc nie będzie to 8 cyfr szesnastkowych dla 4 bajty.
Więc każdy wskaźnik na systemie 32-bitowym będzie 4 bajty,
co oznacza, że ​​w procesie można budować żadnych arbitralnych 4 bajty
i zrobić z tego wskaźnika,
co oznacza, że ​​o ile jest to świadome, może to zająć całe 2 do 32 bajtów pamięci.
Mimo, że tak naprawdę nie ma dostępu do tego,
nawet jeśli komputer ma tylko 512 megabajtów, to myśli, że ma tego dużo pamięci.
A system operacyjny jest na tyle sprytny, że to będzie tylko przydzielić co faktycznie potrzebują.
To nie po prostu przejść, oh, nowy proces: 4 koncerty.
>> Tak. >> [Uczeń] Co wół na myśli? Dlaczego piszesz?
To jest po prostu symbolem szesnastkowym.
Kiedy widzisz numer startowy z wołu, kolejne rzeczy są szesnastkowym.
[Uczeń] Miałeś wyjaśnić, co dzieje się, gdy program się kończy. >> Tak.
Co się dzieje, gdy program się kończy to system operacyjny
tylko usuwa mapowania, że ​​ma dla tych adresów, i to jest to.
System operacyjny może teraz po prostu dać tej pamięci do innego programu do używania.
[Uczeń] Dobra.
Więc jeśli coś przeznaczyć na stercie lub stosu lub zmienne globalne lub cokolwiek,
wszyscy zniknąć natychmiast kończy program
ponieważ system operacyjny jest już wolny, aby dać tej pamięci do innego procesu.
[Uczeń] Mimo że prawdopodobnie istnieją jeszcze wartości pisane? >> Tak.
Wartości mogą nadal.
To jest po prostu to będzie trudno dostać się do nich.
Jest o wiele bardziej trudne do uzyskania na nich, niż to dostać w usuniętego pliku
ponieważ usunięta rodzaj pliku siedzi tam przez długi czas, a dysk twardy jest dużo większy.
Tak to się dzieje, aby zastąpić różne części pamięci
zanim się to stanie zastąpić kawałek pamięci, że plik używany do być.
Ale pamięć główna, RAM, możesz przełączać się o wiele szybciej,
tak to się bardzo szybko zastąpione.
Pytania o to czy coś innego?
[Uczeń] Mam pytania o inny temat. >> Okay.
Czy ktoś ma pytania w tej sprawie?
>> Okay. Inny temat. >> [Uczeń] Dobra.
Miałem przez niektóre z praktycznych testów,
w jednej z nich mówił o sizeof
i wartość, która zwraca lub różne typy zmiennych. >> Tak.
I powiedział, że zarówno int i długo zarówno powrotu 4, więc są one zarówno 4 bajtów.
Czy jest jakaś różnica między int i długi, czy jest to samo?
Tak, że różnica.
C standard -
Jestem prawdopodobnie będzie bałagan.
C standard jest po prostu to, co C jest oficjalną dokumentacją C.
To jest to, co mówi.
Więc C standard po prostu mówi, że char będzie zawsze i zawsze będzie 1 bajt.
Wszystko potem - krótki zawsze jest zdefiniowane jako większa niż lub równa znak.
To może być większy od, ale nie jest dodatni.
Int jest zdefiniowane tak jak jest większa niż lub równa krótki.
I tak długo jest zdefiniowane jako większa niż lub równa int.
I długości czasu jest większa niż lub równa długości.
Jedyne więc C Standard definiuje jest względne uporządkowanie wszystkiego.
Rzeczywista ilość pamięci, że wszystko jest na ogół trwać do realizacji,
ale jest całkiem dobrze określone w tym punkcie. >> [Uczeń] Dobra.
Więc szorty są prawie zawsze będzie 2 bajty.
Ints są prawie zawsze będzie to 4 bajty.
Długie wyroby długie są prawie zawsze będzie to 8 bajtów.
I tęskni, to zależy od tego, czy używasz 32-bitowej lub 64-bitowego systemu.
Tak długo będzie odpowiadać typu systemu.
Jeśli używasz 32-bitowego systemu, jak urządzenia, to będzie 4 bajty.
Jeśli używasz 64-bit jak wiele nowszych komputerach, to będzie 8 bajtów.
>> Ints są prawie zawsze 4 bajty na tym punkcie.
Długie wyroby długie są prawie zawsze 8 bajtów.
W przeszłości, w celu Ints używane tylko 2 bajty.
Ale zauważ, że ten całkowicie spełnia wszystkie te relacje, powyżej i równe.
Tak długo, doskonale może być taka sama jak liczba całkowita,
i to również może mieć taki sam rozmiar jak długo długo.
I to właśnie tak dzieje się, że 99,999% systemów, to będzie równa
albo int lub long long. Zależy to tylko od 32-bit czy 64-bit. >> [Uczeń] Dobra.
W pływaków, jak jest przecinek wyznaczone względem bitów?
Lubię jako binarne? >> Tak.
Nie musisz wiedzieć, że dla CS50.
Ty nawet nie dowiedzieć się, że w 61.
Nie dowiedzą się, że tak naprawdę w każdym kursie.
To tylko reprezentacja.
Zapomniałem dokładnie przydziały bitowych.
Pomysł zmiennoprzecinkowych jest przydzielić określoną liczbę bitów do reprezentacji -
Zasadniczo, wszystko jest w notacji naukowej.
Więc przydzielić określoną liczbę bitów do reprezentowania samego numeru, jak 1,2345.
I nigdy nie może reprezentować liczby z więcej niż 5 cyfr.
Wtedy też przydzielić określoną liczbę bitów tak, że wydaje się być jak
można tylko iść do pewnej liczby, jak to jest największy wykładnik można mieć,
i można tylko iść w dół do pewnego wykładnika,
jak to jest najmniejszy wykładnik możesz mieć.
>> Nie pamiętam dokładnie sposób bity są przypisane do wszystkich tych wartości,
ale pewna liczba bitów są dedykowane do 1,2345,
kolejny pewna liczba bitów są dedykowane do wykładnika,
i to tylko możliwe, aby stanowić wykładnik określonej wielkości.
[Uczeń] I podwójnie? Jest to, że jak na dodatkowym pacy? >> Tak.
To samo, co pływaka chyba teraz używasz 8 bajtów zamiast 4 bajtów.
Teraz będziesz mieć możliwość korzystania z 9 cyfr lub 10 cyfr,
i to będzie w stanie iść do 300, a nie 100. >> [Uczeń] Dobra.
I unosi się również 4 bajty. >> Tak.
Cóż, znowu, to chyba zależy od ogólnego w sprawie ogólnego wdrażania,
ale pływaki są 4 bajty, dwuosobowe to 8.
Doubles nazywane są podwójne, ponieważ są one dwukrotnie rozmiar pływaków.
[Uczeń] Dobra. I są tam dwukrotnie podwaja? >> Nie ma.
Myślę - >> [uczeń] Podoba długich tęskni? >> Tak. Myślę, że nie. Tak.
[Uczeń] Na ubiegłorocznym teście było pytanie o głównej funkcji
mające być częścią programu.
Odpowiedź była, że ​​to nie musi być częścią programu.
W jakiej sytuacji? To, co widziałem.
[Bowden] Wydaje się - >> [uczeń] Jaka sytuacja?
Masz problem? >> [Uczeń] Tak, z całą pewnością mogę wyciągnąć go.
To nie musi być, technicznie, ale w zasadzie to będzie.
[Uczeń] Widziałem jednego na inny Rok.
To było jak Prawda czy fałsz: ważne - >> Och, plik c.?
. [Uczeń] Każdy plik c musi mieć - [zarówno w mowie na raz - niezrozumiały]
Okay. Więc to jest oddzielne.
>> Plik. C po prostu musi zawierać funkcje.
Możesz skompilować plik do kodu maszynowego, binarny, cokolwiek,
przy czym nie jest wykonywalny jeszcze.
Ważne wykonywalny musi mieć główną funkcję.
Możesz napisać 100 funkcje w 1 pliku, ale nie najważniejsze
a następnie skompilować, że w dół na dwójkową
następnie napisać inny plik, który ma tylko główny, ale wywołuje kilka tych funkcji
w tym pliku binarnego tutaj.
I tak, gdy robisz wykonywalny, to co robi linker
jest ona łączy te 2 pliki binarne do pliku wykonywalnego.
Tak więc. Pliku C nie musi mieć funkcję głównego ogóle.
I na dużych bazach kodu zobaczysz tysiące plików. C i 1 plik główny.
Więcej pytań?
[Uczeń] Nie było kolejne pytanie.
To powiedział, aby to kompilator. Prawda czy fałsz?
A odpowiedź była fałszywa, a ja zrozumiałem, dlaczego to nie jest tak Clang.
Ale to, co my nazywamy się, jeśli nie jest?
Marka jest w zasadzie tylko - można zobaczyć dokładnie to, co nazywa go.
Ale to tylko wykonywaniem poleceń.
Make.
Mogę ciągnąć tego. Tak.
Oh, yeah. Dodać też robi.
Mówi to celem jest narzędzie make automatycznie określić
która część dużego programu wymaga rekompilacji
i wydawania poleceń przekompilować je.
Można zrobić, aby pliki, które są absolutnie ogromne.
Marka patrzy na znaczniki czasu plików i, jak powiedziałem wcześniej,
można kompilować poszczególne pliki w dół, i to nie jest, aż dojdziesz do linker
że są one połączone do pliku wykonywalnego.
Więc jeśli masz 10 różnych plików i wprowadzić zmiany w 1 z nich,
to co marka ma zamiar zrobić, to po prostu przekompilować że 1 plik
a następnie ponownie połączyć wszystko razem.
Ale jest znacznie głupszy niż to.
To do ciebie, aby całkowicie zdefiniować, że to jest to, co powinno się robić.
Jest domyślnie posiada zdolność do rozpoznawania tych rzeczy czasowe,
ale można zapisać plik make nic robić.
Możesz napisać, aby plik, tak aby po wpisaniu się to tylko cd do innego katalogu.
Byłem sfrustrowany, bo wszystko tack wewnątrz mojego Appliance
a potem wyświetlić PDF z Mac.
>> Więc idę do Findera i mogę zrobić: Idź, Połącz się z serwerem,
i połączyć się z serwerem jest mój Appliance, a potem otworzyć plik PDF
który pobiera kompilowane przez LaTeX.
Ale byłem sfrustrowany, bo za każdym razem musiałem odświeżyć PDF,
Miałem go skopiować do określonego katalogu, który może uzyskać dostęp do
i robiło się irytujące.
Zamiast więc napisałem plik make, trzeba określić, jak to sprawia, że ​​rzeczy.
Jak zrobić to w PDF LaTeX.
Podobnie jak każdy inny plik make - lub Chyba nie widziałeś plików make,
ale mamy w urządzeniu globalny plik sprawiają, że po prostu mówi:
Jeśli kompilujesz plik C, użyj dzyń.
I tak oto w moim pliku sprawi że robię mówię,
ten plik masz zamiar kompilować z PDF LaTeX.
A więc jest to PDF LaTeX, który robi kompilację.
Producent nie jest kompilacją. To jest po prostu wykonywania tych poleceń w kolejności I określony.
Więc działa LaTeX PDF, kopiuje je do katalogu I ma to być skopiowane,
to cd do katalogu i robi inne rzeczy,
ale to nie jest rozpoznać kiedy zmiany plików,
i czy to zmieni, to będzie uruchomić polecenia że to ma działać
gdy zmiany plików. >> [Uczeń] Dobra.
Nie wiem, gdzie globalne pliki zwierne są dla mnie sprawdzić.
Inne pytania? Coś z przeszłości quizy? Wszelkie rzeczy wskaźnik?
Istnieją subtelne rzeczy, ze wskazówkami, jak -
I nie zamierzam być w stanie znaleźć pytanie quizu na nim -
ale tak jak tego typu rzeczy.
Upewnij się, że rozumiesz, że kiedy mówię, int * x * y -
To nie jest dokładnie to, coś tu chyba.
Ale jak * x * y, to są 2 zmienne, które są na stosie.
Kiedy mówię, że x = malloc (sizeof (int)), x jest wciąż zmienna na stosie,
malloc jest jakiś blok na w kupie, a my mając punkt X na stertę.
>> Więc coś na punkty stosu do stosu.
Kiedykolwiek malloc niczego, jesteś nieuchronnie przechowywanie go wewnątrz wskaźnika.
Tak, że wskaźnik jest na stosie, malloced blok jest na stercie.
Wielu ludzi gubi i powiedzieć int * x = malloc; x jest na stercie.
No Co x wskazuje na to na stercie.
x sam w sobie jest na stosie, chyba że z jakiegoś powodu masz x będzie zmienną globalną,
w tym przypadku, aby być w stanie innym regionie pamięci.
Więc śledzenie te skrzynki i strzałka schematy są dość powszechne w quizie.
Lub jeśli nie jest na quizie 0, to będzie na quizie 1.
Powinieneś wiedzieć, wszystkie z nich, kroki w opracowaniu
ponieważ trzeba było odpowiedzieć na pytania dotyczące tych. Tak.
[Student] Czy możemy przejść nad tymi krokami - >> Jasne.
Przed schodami i kompilacji mamy wstępne przetwarzanie,
kompilowania, montaż i łączenie.
Przerób. Co z tego?
To najprostszy krok - dobrze, nie jak -
to nie znaczy, powinno być oczywiste, ale jest to najprostszy krok.
Macie może wdrożyć go sami. Tak.
[Uczeń] Weź to, co masz w pliku zawiera tak i kopiuje, a następnie definiuje również.
To wygląda na takie rzeczy jak # include i # define,
i to po prostu kopie i pasty, co to właściwie znaczy.
Więc kiedy mówisz # include cs50.h, preprocesor jest kopiowanie i wklejanie cs50.h
do tej linii.
Kiedy mówisz # define x być 4, preprocesor przechodzi całego programu
i zastępuje wszystkie wystąpienia X z 4.
Więc preprocesor ma poprawny plik C i wyprowadza poprawny plik C
jeżeli rzeczy zostały skopiowane i wklejone.
Więc teraz kompilacji. Co z tego?
[Uczeń] To idzie z C na binarny.
>> [Bowden] To nie przejść całą drogę na binarny.
[Uczeń] do kodu maszynowego, a następnie? >> To nie jest kod maszynowy.
[Uczeń] Assembly? >> Assembly.
To idzie do Zgromadzenia zanim przejdzie całą drogę do kodu w C,
i większość języków coś jak to zrobić.
Wybrać dowolny język wysokiego poziomu, a jeśli masz zamiar go skompilować,
prawdopodobnie skompilować w krokach.
Pierwszy to będzie skompilować Pythona do C, to to się skompilować C do Zgromadzenia,
a następnie Zgromadzenie będzie tłumaczone na binarny.
Więc kompilacji przyniesie to z C do Zgromadzenia.
Słowo kompilacji zwykle oznacza doprowadzenia z wyższego poziomu
do dolnego poziomu języka programowania.
Więc to jest tylko etap kompilacji, gdzie zacząć języku wysokiego poziomu
i kończy się w języku niskiego poziomu, i dlatego nazywany jest etapem kompilacji.
[Uczeń] Podczas kompilacji, powiedzmy, że zrobiłeś # include cs50.h.
Will kompilator recompile cs50.h jak funkcji, które są tam,
i tłumaczyć, że do kodu Zgromadzenia, jak również,
czy będzie to skopiować i wkleić coś, co zostało wcześniej Zgromadzenie?
cs50.h będzie dość dużo nie kończy się w Zgromadzeniu.
Rzeczy jak prototypów funkcji i rzeczy są tylko dla Ciebie, aby być ostrożnym.
To gwarantuje, że kompilator może sprawdzić takie rzeczy jak dzwonisz funkcji
z odpowiednich typów powrotu i odpowiednich argumentów i rzeczy.
>> Więc cs50.h będzie preprocesowane do pliku, a następnie, kiedy to kompilacja
Jest to w zasadzie wyrzucić po daje pewność, że wszystko jest nazywany pełnym zakresie.
Ale funkcje zdefiniowane w CS50 biblioteki, które są oddzielone od cs50.h,
tych, którzy nie będą osobno skompilowane.
Które rzeczywiście zstąpił na etapie łączenia, więc my się do tego w sekundę.
Ale po pierwsze, to, co jest montaż?
[Uczeń] Montaż na binarny? >> Tak.
Montaż.
Nie nazywamy go kompilacji, ponieważ Zgromadzenie jest prawie czysty tłumaczenie binarny.
Jest bardzo mało logika przechodząc od Zgromadzenia na binarny.
To tak, jak patrzy w górę w tabeli, oh, mamy tej instrukcji;
który odpowiada na binarny 01110.
I tak pliki montaż ogólnie wyjścia są pliki. Ö.
I pliki. O tym, co mówiliśmy wcześniej,
Jak pliku nie jest konieczne, aby główny funkcji.
Każdy plik może być zestawiane w dół do pliku. O ile jest to prawidłowy plik C.
To może być zestawiane w dół. O.
Teraz, łącząc to, co rzeczywiście przynosi bukiet. Pliki o i prowadzi je do pliku wykonywalnego.
A więc to, co robi, to może linking myślisz o CS50 bibliotece pliku. O.
Jest już skompilowany plik binarny.
I tak podczas kompilacji plik, Twój hello.c, która wywołuje GetString,
hello.c pobiera skompilowany dół hello.o,
hello.o jest teraz w formacie binarnym.
Wykorzystuje GetString, więc musi przejść na cs50.o,
i linker smooshes je razem i kopiuje getString do tego pliku
i wychodzi z pliku wykonywalnego, który ma wszystkie funkcje, których potrzebuje.
Więc cs50.o nie jest faktycznie O plik, ale to jest na tyle blisko, że nie ma zasadniczej różnicy.
Więc łączenie tylko przynosi kilka plików razem
że oddzielnie zawierać wszystkie funkcje potrzebne do korzystania z
i tworzy plik wykonywalny, który będzie faktycznie działać.
>> A więc to jest także to, co mówiliśmy przed
gdzie można mieć 1000. plików C, skompilować je wszystkie. Pliki o,
co prawdopodobnie trochę potrwać, a następnie zmienić 1 plik. c.
Trzeba tylko przebudować ten 1 plik. C, a następnie ponownie połączyć wszystkiego innego,
połączyć wszystko razem.
[Uczeń] Gdy mamy połączenie piszemy lcs50?
Tak, tak lcs50. Że sygnały flagę do łącznika, które powinny być wiążące w tej bibliotece.
Pytania?
Czy zaszliśmy nad binarnym innym niż to 5 sekund na pierwszym wykładzie?
Myślę, że nie.
Powinieneś wiedzieć wszystko o wielkim Os że zaszliśmy nad,
i powinny być w stanie, jeśli dał ci funkcji
powinieneś być w stanie powiedzieć, że jest duża O, mniej więcej. Albo dobrze, big O jest szorstka.
Więc jeśli widzisz zagnieżdżone pętli pętli na tej samej ilości rzeczy,
jak int i, i <n; int j, j > [uczeń] n kwadratu. >> To wydaje się być n do kwadratu.
Jeśli potrójna zagnieżdżone, to wydaje się być n do sześcianu.
Tak więc tego typu rzeczy powinny być w stanie wskazać natychmiast.
Musisz znać rodzaj wstawiania i sortowanie bąbelkowe i scalania sortowania i wszystkie z nich.
Łatwiej jest zrozumieć, dlaczego są te n do kwadratu, a n log n a wszystko to
bo myślę, że był na quizu jeden rok, gdzie w zasadzie dał ci
Realizacja sortowanie bąbelkowe i powiedział: "Co to jest czas działania tej funkcji?"
Tak więc, jeśli uznają ją za sortowanie bąbelkowe, to można od razu powiedzieć, n do kwadratu.
Ale jeśli tylko spojrzeć na to, że nawet nie zdawać sobie sprawę, że to coś w rodzaju bańki;
można po prostu powiedzieć, że robi to i to. To jest n do kwadratu.
[Student] Czy są jakieś trudne przykłady można wymyślić,
jak podobny pomysł na zastanawianie się?
>> Nie sądzę, byśmy daje żadnych trudnych przykładów.
Rzeczą sortowanie bąbelkowe jest tak trudne, jak pójdziemy,
a nawet, że tak długo, jak rozumiem, że jesteś iteracja tablicy
dla każdego elementu w tablicy, która ma być coś, co jest n do kwadratu.
Istnieją ogólne pytania, jak tu mamy - Oh.
Tylko na drugi dzień, Doug stwierdził, "I wymyślili algorytm, który może uporządkować tablicę
"Z n liczb w czasie O (log n) czas!"
Więc skąd mamy wiedzieć, że to niemożliwe?
[Niesłyszalne reakcja studentów] >> Tak.
Przynajmniej, trzeba dotknąć każdego elementu w tablicy,
więc jest to niemożliwe, aby sortować tablicę -
Jeżeli wszystko jest w porządku nieposortowanych, potem idziesz do dotykania wszystkiego w tablicy,
więc nie da się to zrobić w mniej niż O n.
[Uczeń] Pokazałeś nam, że przykład jest w stanie to zrobić w O n
jeśli używasz dużo pamięci. >> Tak.
I that's - I zapomnieć, co that's - Czy liczenie sortowanie?
Hmm. Jest to całkowita algorytm sortowania.
Szukałem specjalnej nazwy dla tego, że nie mogłem sobie przypomnieć w zeszłym tygodniu.
Tak. Są to typy, które może wykonać różnego rodzaju rzeczy w Big O n.
Ale istnieją pewne ograniczenia, jak można używać tylko liczb całkowitych do pewnej liczby.
Plus, jeśli próbujesz sortować that's coś -
Jeśli tablica jest 012, -12, 151, 4 mln zł,
wtedy, że jeden element będzie całkowicie zrujnować całą sortowanie.
>> Pytania?
[Uczeń] Jeśli masz funkcji rekurencyjnej i to właśnie sprawia, że ​​wywołania rekurencyjne
w instrukcji return, to tail recursive,
i tak nie będzie, że więcej pamięci podczas wykonywania
czy to przynajmniej użyj porównywalnego pamięć jako iteracyjne rozwiązanie?
[Bowden] Tak.
To prawdopodobnie będzie nieco wolniejszy, ale nie bardzo.
Tail recursive jest całkiem dobra.
Patrząc ponownie na ramki o, powiedzmy, że mamy główną
i mamy int bar (int x), czy coś.
To nie idealne funkcja rekurencyjna, ale bar return (x - 1).
Tak oczywiście, to jest błędne. Musisz przypadki bazowe i takie tam.
Ale idea jest taka, że ​​jest to ogon recursive,
co oznacza, gdy belkę nazywa to dostanie ramkę stosu.
W tej ramce stosu nie będzie mały blok pamięci
że odpowiada jej argument x.
I tak powiedzmy główny dzieje wywołać bar (100);
Więc x zamierza rozpocząć się jak 100.
Jeśli kompilator uznaje, że to ogon funkcja rekurencyjna,
następnie, gdy bar sprawia, że ​​jej wywołanie rekurencyjne do baru,
zamiast tworzyć nową ramkę stosu, który jest gdzie stos zaczyna rosnąć w dużej mierze,
w końcu to będzie działać na stercie, a następnie dostać segfaultów
ponieważ pamięć zaczyna kolizji.
>> Więc zamiast tworzyć własne ramki stosu, może sobie uświadomić,
hej, nigdy naprawdę muszę wrócić do tej ramki stosu,
więc zamiast ja po prostu wymienić ten argument z 99, a następnie uruchomić pasek wszystkim.
A potem zrobi to ponownie i osiągnie pasek powrotu (x - 1),
i zamiast tworzenia nowej ramki stosu, to po prostu zastąpić obecną argumentu z 98
a następnie wrócić do samego początku barze.
Operacje te, zastępując to 1 wartość na stosie i skacze z powrotem do początku,
są dość wydajne.
Tak więc, nie tylko jest to samo zastosowanie jako oddzielne pamięci, która jest funkcją iteracyjny
bo jesteś tylko przy użyciu 1 stosu ramki, ale nie jesteś cierpienie downsides
konieczności wywoływania funkcji.
Funkcje połączeń może być nieco droższe, ponieważ musi zrobić wszystko, to ustawienie
i przerywaniem i wszystkie te rzeczy.
Więc to rekurencja ogon jest dobry.
[Uczeń] Dlaczego nie tworzyć nowe kroki?
Ponieważ realizuje nie musi.
Wezwanie do paska jest właśnie zwrócenie wywołanie rekurencyjne.
Więc nie trzeba robić nic z wartości zwracanej.
To po prostu będzie natychmiast zwrócić.
Więc to po prostu się wymienić swoją argumentację i zacząć od nowa.
A także, jeśli nie mają ogon rekurencyjną wersję,
wtedy otrzymasz wszystkie te bary, w których, gdy ten bar zwraca
ma wrócić jego wartość do tego, to bar natychmiast powraca
i zwraca jego wartość do tego, to jest to po prostu się do natychmiastowego zwrotu
i zwraca jego wartość do tego.
Więc oszczędność to popping wszystkie te rzeczy off stosu
ponieważ zwracana wartość jest tylko będzie przekazywane z powrotem do góry i tak.
Więc dlaczego nie po prostu zastąpić naszą argumentację zaktualizowanego argument i zacząć od początku?
Jeśli funkcja nie tail recursive jest, jeśli można zrobić coś takiego -
[Uczeń], jeśli bar (x + 1). >> Tak.
>> Więc jeśli umieścić go w stanie, to robisz coś z wartości zwracanej.
Lub nawet jeśli tylko zrobić zwrot 2 * bar (x - 1).
Więc teraz bar (x - 1) musi wrócić w celu użycia go do obliczenia 2 razy tej wartości,
więc teraz nie potrzebuje osobne ramki stosu,
i teraz, nie ważne jak bardzo się starasz, masz zamiar trzeba -
To nie jest ogon rekurencyjne.
[Uczeń] Chciałbym spróbować wnieść rekursji do dążyć do rekursji ogonowej -
[Bowden] W idealnym świecie, ale w CS50 nie muszą.
W celu uzyskania rekurencji ogon, ogólnie, należy założyć dodatkowy argument
gdzie bar będzie int x do y.
a y odpowiada ostatecznej rzeczą, którą chcesz, aby powrócić.
Więc to będziesz wrócimy bar (x - 1), 2 * y.
Tak, że to tylko na wysokim poziomie, jak przekształcić rzeczy się ogon rekurencyjne.
Ale dodatkowy argument -
A potem w końcu po osiągnięciu przypadku bazowego, po prostu wrócić y
dlatego, że gromadziły się przez cały czas, zwracanej wartości, które chcesz.
Jesteś rodzaju robią to iteracyjnie ale stosując cykliczne rozmowy.
Pytania?
[Uczeń] Może o arytmetyce wskaźnika, podobnie jak przy użyciu ciągów. >> Jasne.
Arytmetyczna wskaźnika.
Podczas korzystania z ciągów jest łatwe, ponieważ ciągi są char gwiazdy,
znaki są zawsze i zawsze jeden bajt,
i tak arytmetyczna wskaźnika odpowiada regularnie arytmetyki, gdy masz do czynienia z ciągów.
Powiedzmy, char * s = "hello".
Mamy więc blok w pamięci.
Potrzebuje 6 bajtów, ponieważ zawsze trzeba null terminatora.
I char * s będzie wskazywać na początku tablicy.
Więc ów wskazuje tam.
Teraz, to jest w zasadzie jak każda tablica działa
niezależnie od tego, czy to powrót przez malloc czy to na stosie.
Każda tablica jest zasadniczo wskaźnik początku tablicy
a każde działanie tablicy, każdy indeksowanie, jest po prostu wchodząc w tej tablicy pewne przesunięcie.
>> Więc kiedy mówię coś jak s [3], to będzie s i liczenia 3 znaki cala
Więc s [3], mamy 0, 1, 2, 3, więc s [3] ma zamiar odnieść się do tej l.
[Uczeń] A mogliśmy osiągnąć taką samą wartość, wykonując s + 3, a następnie gwiazda nawiasy?
Tak.
Jest to równoważne * (S + 3);
i że jest zawsze i zawsze równoważne nieważne co robisz.
Nigdy nie należy używać składni montażowej.
Zawsze możesz użyć * (y + 3) składni.
Ludzie mają tendencję do jak składni wspornika, choć.
[Uczeń] Więc wszystkie tablice są właściwie tylko wskaźniki.
Istnieje niewielka różnica, kiedy mówię, int x [4] >> [Student] Czy aby utworzyć pamięć?
[Bowden] To zamierza stworzyć 4 wskazówki na stosie, więc 16 bajtów ogólnej.
To będzie tworzyć 16 bajtów na stosie.
x nie jest nigdzie zapisane.
Jest to po prostu symbol myśli początku rzeczy.
Ponieważ zadeklarował tablicę wewnątrz tej funkcji,
co kompilator ma zamiar zrobić to zamienić wszystkie wystąpienia zmiennej x
w którym to się stało, aby wybrać, aby umieścić te 16 bajtów.
Nie można tego zrobić z char * s, ponieważ s jest rzeczywisty wskaźnik.
To jest wolny, aby następnie wskazać na inne rzeczy.
x jest stała. Nie można mieć punkt do innej tablicy. >> [Uczeń] Dobra.
Ale ten pomysł, to indeksowanie, jest taka sama, niezależnie od tego czy jest to tradycyjna tablica
lub jeśli jest to wskaźnik do czegoś lub jeśli jest to wskaźnik do malloced tablicy.
I rzeczywiście, tak jest, że odpowiada również samo.
To właściwie tylko tłumaczy, co jest wewnątrz wsporników i co z uchwytami
dodaje je razem, a dereferences.
Tak jak jest to ważne w * (+ 3 s) lub S [3].
[Student] Czy masz wskaźniki wskazujące na tablicach 2-wymiarowe?
>> Jest trudniej. Tradycyjnie, nie.
2-wymiarowa tablica jest tylko 1-wymiarowa tablica z jakimś dogodnym składni
ponieważ kiedy mówię int x [3] [3], jest to tak naprawdę tylko 1 tablica z 9 wartości.
I tak, kiedy wskaźnik, kompilator wie, co mam na myśli.
Jeśli powiem, że x [1] [2], to wie, że chcę iść do drugiego rzędu, więc to będzie pominąć pierwszy 3,
i chce drugą rzecz w tym, więc to będzie dostać ten jeden.
Ale to nadal jest tylko jedno-wymiarowej tablicy.
I tak, jeśli chciałem przypisać wskaźnik do tej tablicy,
Chciałbym powiedzieć, int * p = x;
Typu X jest po prostu -
Jest szorstki mówiąc typ x, ponieważ to tylko symbol i nie jest to rzeczywista zmienna,
ale to tylko int *.
x jest tylko wskaźnik na początku tego. >> [Uczeń] Dobra.
I tak nie jest w stanie przejść [1] [2].
Myślę, że jest specjalna składnia deklarowania wskaźnik,
coś śmieszne jak int (* p [-. coś absolutnie śmieszne Ja nawet nie wiem.
Ale jest składnia deklarowania wskazówek jak z nawiasami i rzeczy.
Nie może nawet na to pozwolić.
Mogę spojrzeć na coś, co chciał mi powiedzieć prawdę.
Ja patrzę na to później, jeśli jest składnia punktu. Ale nigdy nie będzie to widział.
I nawet składnia jest tak archaiczny, że jeśli używasz go, ludzie będą zdumieni.
Tablice wielowymiarowe są dość rzadkie, jak to jest.
Jesteś całkiem dużo -
Cóż, jeśli robisz rzeczy matrycy nie będzie rzadkością,
ale w C masz rzadko zamiar używać tablice wielowymiarowe.
Tak. >> [Uczeń] Powiedzmy, że mają naprawdę długą tablicę.
>> Więc w pamięci wirtualnej wydaje się być wszystko konsekutywne,
jak elementów tuż obok siebie,
a w pamięci fizycznej, byłoby możliwe, że do rozdrabniania się? >> Tak.
Jak działa pamięć wirtualna to tylko oddziela -
Jednostka alokacji jest strona, która ma tendencję do 4 kilobajtów,
i tak, gdy proces mówi, hej, chcę używać tej pamięci,
System operacyjny ma zamiar przeznaczyć to 4 kilobajtów dla tego małego bloku pamięci.
Nawet jeśli używasz tylko jednego małego bajt całego bloku pamięci,
system operacyjny będzie dać mu pełne 4 kilobajtów.
Więc oznacza to, mógłbym - powiedzmy to jest mój stosu.
Ten stos mogą być rozdzielone. Mój stack może być megabajtów i megabajty.
Mój stosu mogą być ogromne.
Ale sam stos musi być podzielona na kilka pojedynczych stron,
które, jeśli przyjrzymy się tutaj powiedzmy, to jest nasz RAM,
jeśli mam 2 gigabajty pamięci RAM, jest to rzeczywista 0 adres jak 0-gie bajcie mojej pamięci RAM,
i jest to 2 gigabajty całą drogę tutaj.
Więc ta strona może odpowiadać tym bloku tutaj.
Ta strona może odpowiadać tym bloku tutaj.
Ten jeden może odpowiadać ten jeden tutaj.
Więc system operacyjny jest wolny przypisać pamięci fizycznej
do każdej indywidualnej strony arbitralnie.
A to oznacza, że ​​jeśli ta granica stanie się plasujące tablicę,
array dzieje się po lewej i prawej stronie tego porządku na stronie,
następnie, że tablica ma być podzielony w pamięci fizycznej.
A następnie po zamknięciu tego programu, gdy proces kończy się
Odwzorowania te uzyskać usunięte i wtedy jest do korzystania z tych małych bloków dla innych rzeczy.
Więcej pytań?
[Uczeń] arytmetyczna wskaźnika. >> Oh yeah.
Struny były łatwiejsze, ale patrząc na coś takiego jak wskazówki,
więc z powrotem do int x [4];
Czy jest to tablica lub czy jest to wskaźnik do tablicy 4 malloced liczb całkowitych,
to będzie traktowany tak samo.
[Uczeń] Więc tablice są na stercie?
[Bowdena] Tablice nie są na stercie. >> [Uczeń] Oh.
>> [Bowden] macierzy tego typu jest zazwyczaj na stosie
chyba że uznana go na - ignorowanie zmiennych globalnych. Nie należy używać zmiennych globalnych.
Wewnątrz funkcji mówię int x [4];
To będzie stworzenie 4-całkowitą blokadę na stosie dla tej tablicy.
Ale to malloc (4 * sizeof (int)); ma zamiar iść na stercie.
Ale po tym momencie można użyć X i P w prawie tych samych sposobów,
poza wyjątkami mówiłem o można przypisać p.
Technicznie, ich rozmiary są nieco inne, ale to jest zupełnie nieistotne.
Nigdy właściwie wykorzystywać swoje rozmiary.
P mogę powiedzieć p [3] = 2, lub x [3] = 2;
Można ich używać w dokładnie tych samych metod.
Więc arytmetyczna wskaźnika teraz - Tak.
[Uczeń] Nie musisz zrobić * p jeśli masz konsole?
Wsporniki są niejawne dereference. >> Okay.
Właściwie, także to, co mówisz, ze można dostać tablice wielowymiarowe
ze wskaźnikami, co możesz zrobić, to coś jak, powiedzmy, int ** pp = malloc (sizeof (int *) * 5);
Ja po prostu piszę to wszystko w pierwszej kolejności.
Nie chcę, że jeden.
Okay.
Co ja tutaj jest - To powinno być pp [i].
Więc pp jest wskaźnik do wskaźnika.
Jesteś mallocing pp zwrócić do tablicy 5 int gwiazd.
Więc w pamięci masz na pp stosu
To będzie punkt do tablicy 5 bloków, które wszystkie są same wskaźniki.
A potem, kiedy malloc tu, I malloc, że każdy z tych poszczególnych wskaźników
powinien wskazywać na osobny blok 4 bajtów na stercie.
Więc to wskazuje na 4 bajtach.
A ten wskazuje na inny 4 bajtów.
>> I wszystkie z nich wskazują na własnych 4 bajtów.
To daje mi sposób prowadzenia wielowymiarowych rzeczy.
Mógłbym powiedzieć, pp [3] [4], ale teraz nie jest to samo, co tablice wielowymiarowe
bo wielowymiarowymi tablicami to tłumaczone [3] [4] w jeden przesunięcie do tablicy x.
To p dereferences, dostęp do trzeciego indeksu, a następnie dereferences że
i odwiedziny - 4 byłyby nieważne - drugi indeks.
Mając na uwadze, kiedy mieliśmy int x [3] [4] przed jako wielowymiarową tablicą
i po dwukrotnym wspornik to naprawdę tylko jeden dereference,
jesteś po jednego wskaźnika, a następnie przesunięcie,
to jest naprawdę 2D referencje.
Możesz śledzić 2 oddzielne wskaźniki.
Więc to również technicznie pozwala mieć tablice wielowymiarowe
gdzie każdy tablica jest różne rozmiary.
Więc myślę, że poszarpane wielowymiarowymi tablicami, jest to, co się nazywa
ponieważ naprawdę pierwszą rzeczą mogą wskazywać na coś, co ma 10 elementów,
Druga sprawa może wskazywać na coś, co ma 100 elementów.
[Student] Czy istnieje ograniczenie liczby wskaźników można mieć
wskazując na inne wskaźniki? No >>
Możesz mieć int p *****.
Powrót do arytmetyki wskazówkach - >> [uczeń] Oh. >> Tak.
[Uczeń] Jeśli mam int *** P, a potem zrobić dereferencji i mówię * p jest równa tej wartości,
Czy jest to tylko zamiar zrobić 1 poziom dereferencji? >> Tak.
Więc jeśli chcesz mieć dostęp do rzeczy, że ostatni wskaźnik jest wskazując na -
Następnie zrobić P ***. >> Okay.
Więc to jest punkty P do 1 bloku, punkty do innego bloku, wskazuje na innego bloku.
Wówczas, jeśli * p = coś innego, to są zmiany tego
Dotychczas wskazać innego bloku. >> Okay.
>> [Bowden] A jeśli były one malloced, to masz teraz pamięć wyciekła
chyba że zdarzy ci się mieć różne odniesienia do tych
skoro nie można dostać się z powrotem do tych z nich, że po prostu wyrzucił.
Arytmetyczna wskaźnika.
int x [4]; zamierza przeznaczyć tablicę 4 liczb
gdzie X będzie wskazywać na początku tablicy.
Więc kiedy mówię coś jak X [1], chcę go do myśli przejść do drugiego całkowitą w tablicy,
które może ta.
Ale tak naprawdę, to 4 bajty do tablicy ponieważ całkowita zajmuje 4 bajty.
Więc przesunięcie 1 naprawdę oznacza przesunięcie 1
razy większe bez względu na rodzaj tablicy jest.
To jest tablica liczb całkowitych, więc wie, zrobić 1 razy rozmiar int gdy chce przesunięcia.
Inna składnia. Pamiętaj, że jest to równoważne * (x + 1);
Kiedy mówię, że wskaźnik + 1, co to jest adres wraca, że ​​wskaźnik jest przechowywanie
plus 1 razy wielkość typu wskaźnika.
Więc jeśli x = OX100, to x + 1 = ox104.
I można nadużywać i powiedzieć coś jak char * c = (char *) x;
a teraz c będzie adres jak x.
c będzie równa OX100,
a c + 1 będzie równe ox101
od arytmetyczna wskaźnika zależy od rodzaju wskaźnika, który jest dodawany do.
Więc c + 1, to wygląda na około, to wskaźnik char, więc to będzie dodać 1 razy rozmiar char,
która zawsze będzie 1, więc masz 101,
mając na uwadze, jeśli mam zrobić X, który również jest nadal 100, x + 1 będzie 104.
[Uczeń] Możesz użyć C + +, w celu poszerzenia swojej wskaźnik przez 1?
Tak, można.
Nie można tego zrobić z powodu x x to tylko symbol, to jest stała, nie można zmienić x.
>> Ale c dzieje po prostu wskaźnik, więc c + + jest całkowicie poprawny i będzie to przyrost o 1.
Jeśli c były po prostu int *, to c + + będzie 104.
+ + Czy arytmetyka wskaźnik tak jak c + 1 zrobiłby arytmetyczne wskaźnika.
To jest rzeczywiście, jak wiele rzeczy, takich jak sortowanie korespondencji seryjnej -
Zamiast tworzyć kopie rzeczy, można zamiast tego przekazać -
Jakbym chciał przekazać tę połowę tablicy - niech skasować niektóre to.
Powiedzmy, że chciałem przekazać tę stronę tablicy do funkcji.
Co chciałbym przejść do tej funkcji?
Jeśli zdam x, jestem zdania tego adresu.
Ale chcę, aby przekazać ten konkretny adres. Więc co mam przejść?
[Uczeń] Pointer + 2?
[Bowden] Więc x + 2. Tak.
To będzie ten adres.
Będziesz także bardzo często postrzegają ją jako x [2], a następnie adres to.
Więc musisz mieć adres, bo uchwyt jest niejawna dereference.
x [2] odnosi się do wartości, która jest w tym polu, a potem chcesz, aby adres tej skrzynki,
tak mówisz & x [2].
Więc to jest jak coś w rodzaju łączenia, w której chcesz przekazać pół listę do czegoś
naprawdę tylko przejść & x [2], a teraz aż wywołanie rekurencyjne jest zaniepokojony,
moja nowa tablica zaczyna się tam.
Ostatnie pytania minut.
[Uczeń] Jeśli nie położymy ampersand lub - co to się nazywa? >> Star?
[Uczeń] Star. >> Technicznie, operator dereference, ale - >> [uczeń] dereference.
>> Jeśli nie położymy gwiazdę lub handlowego i, co się stanie, jeśli po prostu powiedzieć, y = x, x jest wskaźnik?
Co to jest typ Y? >> [Uczeń] Powiem tylko, że to wskaźnik 2.
Więc jeśli tylko powiedzieć, y = x, x, a teraz punkt y do tej samej rzeczy. >> [Uczeń] Punkt do tej samej rzeczy.
A jeśli x jest int wskaźnik? >> By narzekać, ponieważ nie można przypisać wskaźniki.
[Uczeń] Dobra.
Pamiętaj, że wskaźniki, choć zwracamy je jako strzałki,
naprawdę wszystko, sklep - int * x - naprawdę wszystko x jest przechowywanie jest czymś OX100,
które się do reprezentowania w bloku wskazując przechowywane w 100.
Więc kiedy mówię, int * y = x; Ja tylko kopiowanie OX100 w y,
które po prostu będzie reprezentować jako y, także wskazując OX100.
A jeśli powiem, int i = (int) x; następnie i będzie przechowywać, co jest wartością OX100
wewnątrz niego, ale teraz to będzie interpretowane jako liczba całkowita zamiast wskaźnika.
Ale trzeba do obsady albo będzie narzekać.
[Uczeń] Więc to znaczy oddać -
Czy to będzie odlewania int int x lub y odlew?
[Bowden] Co?
[Uczeń] Dobra. Po tych nawiasach jest tam będzie x lub aj tam?
>> [Bowden] Albo. X i Y są równoważne. >> [Uczeń] Dobra.
Bo są oba wskaźniki. >> Tak.
[Uczeń] Tak byłoby przechowywać szesnastkową 100 w całkowitej formie? >> [Bowden] Tak.
Ale wartość cokolwiek to wskazuje.
[Bowden] Tak. >> [Uczeń] Więc po prostu adres w postaci liczby całkowitej. Okay.
[Bowden] Jeśli chciał jakiegoś dziwacznego powodu
można zająć się wyłącznie wskaźniki i nigdy do czynienia z liczbami całkowitymi
i po prostu być jak int * x = 0.
Wtedy będziesz się naprawdę pomylić raz arytmetyka wskaźnik zaczyna dzieje.
Więc numery ich przechowywania są bezsensowne.
To jest po prostu, jak skończy się ich interpretacji.
Więc jestem wolny skopiować OX100 z int * na int,
i jestem wolny, aby przypisać - Jeste prawdopodobnie będzie się krzyknął na nie Casting -
Jestem wolny, aby przypisać coś (int *) ox1234 w ten arbitralny * int.
Więc ox123 jest tak samo ważne jak to jest adres pamięci & y.
& Y dzieje się powrócić, że coś jest bardzo dużo ox123.
[Student] Czy tak jest naprawdę fajny sposób, aby przejść od szesnastkowy do postaci dziesiętnej,
podoba, jeśli masz wskaźnik i używasz go jako int?
[Bowden] Można naprawdę tylko drukowanie za pomocą tak jak printf.
Powiedzmy, że mam int y = 100.
Więc printf (% d \ n - jak powinien już wiesz - drukuj, że jak Integer, x%.
My po prostu wydrukować go w postaci szesnastkowej.
Tak więc wskaźnik nie jest przechowywana jako szesnastkowy
i całkowita nie jest przechowywany jako przecinku.
Wszystko jest przechowywane jako binarny.
To jest po prostu, że mamy tendencję, aby pokazać, jak wskaźniki szesnastkowo
ponieważ uważamy, że rzeczy w tych blokach 4-bajtowych,
i adresy pamięci wydają się być znajome.
Jesteśmy jak, jeśli zaczyna się od bf, to dzieje się na stosie.
Więc jest to tylko nasza interpretacja wskaźników jak szesnastkowo.
Okay. Jakieś ostatnie pytania?
>> Będę tu na trochę po jeśli masz coś innego.
I to jest koniec tego.
>> [Uczeń] Yay! [Oklaski]
>> [CS50.TV]