[Powered by Google Translate] [Secțiunea 5 - mai confortabil]
[Rob Bowden - Universitatea Harvard]
[Acest lucru este CS50. - CS50.TV]
>> Cum am spus, în email-ul meu, există o mulțime de lucruri pe care le puteți utiliza
altele decât aparatul pentru a face de fapt, seturi de probleme.
Vă recomandăm să o faceți în aparat doar pentru că atunci ne putem mai ușor să te ajut
și știm cum totul va merge.
Dar, ca un exemplu de unde poti sa faci lucruri în cazul în care, spun, nu aveți acces la
la un aparat sau dacă doriți să lucrați în subsol Science Center -
care de fapt au aparat prea -
în cazul în care doriți să lucrați oriunde.
Un exemplu este ati vazut / auzit de SSH?
SSH este de fapt la fel ca conecta la ceva.
De fapt, chiar acum mă SSHed în aparat.
N-am să lucrați direct în aparat.
Aici este aparatul, iar daca te uiti aici veți vedea această adresă IP.
N-am lucra în aparatul în sine;
Eu vin mereu pe la o fereastră iTerm2 / terminal de fereastra.
Poți SSH pentru că adresa IP, ssh jharvard@192.168.129.128.
Îmi amintesc că numărul foarte ușor, pentru că este un astfel de model frumos.
Dar asta mă va cere parola, iar acum eu sunt în aparat.
Practic, în acest moment, în cazul în care aveți deschis un terminal interiorul aparatului în sine,
această interfață, cu toate acestea v-ar folosi, este exact la fel
ca interfață Sunt folosind aici, dar acum esti SSHed.
Nu trebuie să SSH la aparatul.
Un exemplu de un alt loc unde ar putea să SSH este Sunt destul de sigur că aveți în mod implicit -
Oh. Mai mare.
Toți ar trebui să aveți conturi de FAS implicit pe serverele FAS.
Pentru mine, aș SSH la rbowden@nice.fas.harvard.edu.
O să te întreb că prima dată, și tu spui da.
Parola mea este doar de gând să fie parola mea FAS.
Și acum, am SSHed la serverele frumos, si eu pot face orice vreau pe aici.
O mulțime de clase s-ar putea lua, cum ar fi 124, sunt de gând să aibă de a încărca chestii de aici
de fapt, să-și prezinte seturi de problema ta.
Dar spune ca nu au acces la aparatul dumneavoastră.
Apoi, puteți face lucruri, cum ar fi pe aici se va spune -
Aceasta este doar secțiunea noastră de întrebări.
Acesta vă va cere să faceți acest lucru în aparat.
În schimb voi face doar pe server.
Am de gând să unzip asta.
Problema va fi că v-ați obișnuit să utilizați ceva de genul gedit
sau orice interiorul aparatului.
Nu te duci să aibă ca pe serverul de consultanță agricolă.
E totul va fi doar această interfață text.
Deci, ai putea fie una, încercați să învețe un editor de text pe care le posedam.
Ei au Nano.
Nano este, de obicei, destul de ușor de utilizat.
Aveți posibilitatea să utilizați săgețile tale și tastați în mod normal.
Așa că nu e greu.
Dacă doriți să obțineți cu adevărat de lux puteti folosi Emacs,
care, probabil, nu ar fi deschis pentru că nici măcar nu știu cum să închidă Emacs.
De control X, de control C? Da.
Sau puteți folosi Vim, care este ceea ce am folosi.
Și astfel acestea sunt optiunile tale.
Dacă nu vrei să faci asta, puteți, de asemenea, daca te uiti la manual.cs50.net--
Oh.
Pe un PC, puteți SSH folosind PuTTY,
care ai de gând să aibă de a descărca separat.
Pe un Mac, puteți pur și simplu prin utilizarea Terminal implicit sau puteți descărca iTerm2,
care este ca un terminal frumos, fantezie.
Dacă te duci la manual.cs50.net veți vedea un link către Notepad + +,
care este ceea ce se poate folosi pe un PC.
Acesta vă permite să SFTP din Notepad + +, care este, în principiu SSH.
Ceea ce vă va permite să faceți este să editați fișiere local,
și apoi ori de câte ori doriți să le salvați, acesta va salva în nice.fas,
în cazul în care puteți rula apoi le.
Și echivalentul pe un Mac va fi TextWrangler.
Deci, vă permite să faceți același lucru.
Acesta vă permite să editați fișiere local și salvați-le la nice.fas,
în cazul în care puteți rula apoi le.
Deci, dacă sunteți vreodată blocat, fără un aparat, aveți aceste opțiuni
încă de a face seturi de problema ta.
Singura problemă va fi că nu sunteți de gând să aibă bibliotecă CS50
deoarece nice.fas nu are în mod implicit că are.
Puteți descărca fie biblioteca CS50 -
Nu cred că am nevoie de la acest punct.
Puteți descărca fie biblioteca CS50 și copiați-l pe nice.fas,
sau cred că în acest moment nu-l utilizați mai oricum.
Sau dacă o facem, puteți pentru moment înlocuiți-l cu
implementările de funcții în biblioteca CS50 oricum.
Așa că nu ar trebui să fie faptul că de mult de o restricție.
Și cu asta basta.
>> Mă duc înapoi la aparatul acum, vom face totul în aparat.
Privind la secțiunea noastră de întrebări, la început, așa cum am spus în email-ul meu,
trebuie sa vorbim despre scurt cel pe care ar fi trebuit sa ma uit.
Avem redirecționarea & Țevi și aceste trei întrebări.
>> Pentru ce curs de apa nu funcționează cum ar fi printf scrie în mod implicit?
Deci curs de apa. Ce este un curs de apa?
Un curs de apa este de fapt ca e doar o -
Nu e chiar o sursă de 1s și 0s.
Curs de apa se cere aici este în afara standard.
Și așa de iarnă este un curs de apa, care, atunci când scrie în ea,
apare pe ecran.
Out standard, prin curs de apa, aceasta înseamnă că scrie doar 1 si 0 la acesta,
și celălalt capăt al out standard, doar se citește de la care curge.
E doar un șir de 1 si 0.
Puteți scrie la fluxuri sau puteți citi la fluxurile
în funcție de ceea ce este de fapt curs de apa.
Celelalte două fluxuri implicite sunt standard în și eroarea standard.
Standard în ori de câte ori este faci getString, se așteaptă să chestii de intrare.
Așa că vă așteaptă, este de fapt, așteaptă standard în,
care este într-adevăr ceea ce obții atunci când tastați la tastatura.
Te tastarea în iarnă inch
Eroarea standard este de fapt echivalent cu standardul afară,
dar e specializată în că, atunci când imprimați eroarea standard,
tu ar trebui să imprimați numai mesaje de eroare la
astfel încât să puteți diferenția între mesaje periodice tipărite la ecran
comparativ cu mesaje de eroare, în funcție de faptul dacă s-au dus la out standard sau eroarea standard.
Fișiere prea.
Out standard, standard în, și eroarea standard sunt fluxuri de doar speciale,
dar de fapt orice fișier, atunci când deschideți un fișier, acesta devine un curs de apa de octeți
în cazul în care puteți citi doar de la acel curs de apa.
Tu, pentru cea mai mare parte, se poate gândi doar a unui fișier ca un flux de octeti.
Deci, fluxuri ce se scrie în mod implicit? Standard afară.
>> Care este diferența între> și >>?
Credeți cineva a viziona videoclipul în prealabil? Bine.
> Va fi modul în care redirecționați în fișiere,
și, de asemenea, se va >> pentru a redirecționa de ieșire în fișiere,
dar e loc de gând să anexeze la dosar.
De exemplu, să presupunem că se întâmplă să aibă dict chiar aici,
și chestii doar în interiorul dict este pisica, pisica, caine, pește, câine.
O singură comandă pe care le avea la linia de comandă este pisica,
care este doar de gând să imprima ceea ce este într-un fișier.
Deci, atunci când spun dict pisica, o să imprimați pisica, pisica, caine, pește, câine. Asta e tot ce face pisica.
Asta înseamnă că imprimat cu standardul din pisica, pisica, caine, pește, câine.
Dacă eu vreau să redirecționeze în schimb faptul că într-un fișier, pot folosi> și redirecționarea-l la orice fisier este.
Voi suna fișierul fișierul.
Deci, acum, dacă I ​​ls, voi vedea am un nou fisier numit fișier.
Și dacă l-am deschis, acesta va avea exact ceea ce pisica pune la linia de comandă.
Deci, acum, dacă eu fac asta din nou, atunci va redirecționa de ieșire într-un fișier,
și am de gând să aibă exact acelasi lucru.
Deci punct de vedere tehnic, aceasta complet anulate ceea ce am avut.
Și vom vedea dacă am schimba dict, am scos câinele.
Acum, dacă am pisica dict într-un fișier din nou, vom avea noua versiune cu câinele eliminat.
Așa că-l suprascrie complet.
În schimb, dacă vom folosi >>, o să adăugați fișierul.
Acum, deschide fișierul, vedem avem doar același lucru de două ori tipărite
pentru că a fost acolo o dată, apoi am anexat la original.
Deci, asta e ceea ce> si >> face.
Are urmatorul cere - Nu întreba despre asta.
>> Celălalt pe care o avem este <, care, dacă> redirecționează afară de iarnă,
<Se va redirecționa standard de inch
Să vedem dacă avem un exemplu.
Pot să scriu unul foarte repede.
Să luăm orice fișier, hello.c.
Relativ simplă fișier.
Primesc doar un șir și apoi tipărirea "Bună ziua", oricare ar fi șirul tocmai am intrat a fost.
Deci, asigurați-Bună ziua și apoi / salut..
Acum ma determinat să intre ceva,
ceea ce înseamnă că se așteaptă ca lucrurile să fie introduse în standardul inch
Deci, ce vreau să introduceți în iarnă inch Vom doar de gând să spun Bună ziua, Rob!
Atunci e imprimarea cu standardul afară Salut, Rob!
Dacă fac / salut. Și apoi redirecționați,
De acum puteți redirecționa numai dintr-un fișier.
Deci, dacă am pus într-un dosar, txt, și am pus Rob,
dacă am alerga salut și apoi redirecționați fișier txt în. / Bună ziua, o să spun Bună ziua, Rob! imediat.
Când se ajunge la primul getString și-l așteaptă pe standard în,
standard în nu mai așteaptă pe tastatură pentru a obține datele introduse.
În schimb, ne-am redirecționat standard pentru a citi din fisier txt.
Și așa se va citi din fisierul txt, care este doar Rob linie,
și apoi o să imprimați Salut, Rob!
Și dacă am vrut, aș putea face, de asemenea. / Bună ziua <txt
și apoi standard, că e de imprimare, care este Buna ziua, Rob!,
Eu pot redirecționa că în dosarul său.
Voi suna doar fișierul salut - nu, nu voi, pentru că e executabil - txt2.
Acum, txt2 va avea ieșire de / salut. <Txt, care va fi Salut, Rob!
>> Întrebări?
>> Bine. Deci aici avem de conducte.
Țevile sunt ultima unitate de redirecționare.
>> Oh. Cred că o unitate de redirectionare este mai în cazul în care în loc de> ce faci 2>,
care este redirecționarea eroarea standard.
Deci, în cazul în care ceva nu a mers la eroarea standard, aceasta nu ar fi pus în txt2.
Dar observați dacă fac 2>, apoi se imprimă încă Salut, Rob! la linia de comandă
pentru că eu sunt doar redirecționarea eroare standard, nu am redirecționarea standard de afară.
Eroarea standard și din iarnă sunt diferite.
Dacă ați fi dorit să scrie de fapt la eroarea standard,
apoi am putea schimba acest lucru să fie fprintf la stderr.
Deci printf, implicit, pentru a imprima în standard.
Dacă vreau să imprimați eroare standard manual, atunci trebuie sa folosesc fprintf
și să specifice ceea ce vreau să imprimați.
Dacă în schimb am făcut stdout fprintf, atunci asta e practic echivalent cu printf.
Dar fprintf la eroarea standard.
Deci, acum, dacă aș redirecționa acest lucru în txt2, Buna, ziua, Rob! este încă obtinerea tipărită la linia de comandă
din moment ce din ce in ce tipărite la eroarea standard și eu sunt doar redirecționarea standard de afară.
Dacă aș redirecționa acum eroarea standard, acum aceasta nu sa imprimat, și txt2 va fi Salut, Rob!
Deci, acum, aveți posibilitatea de a imprima erorile tale reale de a erorii standard
și imprimați mesajele dvs. regulate pentru a out standard.
Și așa, atunci când rulați programul tău, îl puteți rula ca / ​​salut. Acest tip cu 2>
astfel că programul dvs. este de gând să ruleze în mod normal,
dar orice mesaje de eroare pe care le veți obține puteți verifica mai târziu în jurnalul de erori de dvs.,
atât de erori, si uita-te apoi mai târziu și fișierul erori va avea orice erori care au avut loc.
>> Întrebări?
>> Ultima este țeavă, pe care vă puteți gândi ca ia standard, de la o comandă
și făcându-l standard în al următoarea comandă.
Un exemplu este aici ecoul este o linie de lucru de comandă
care este doar de gând să răsune tot ce mi-am pus ca argument. Nu voi pune ghilimele.
Echo bla, bla, bla este doar de gând să imprima bla, bla, bla.
Înainte, când am spus că trebuie să pun Rob într-un fișier txt
pentru că eu pot redirecționa numai fișiere txt, în schimb, / dacă aș repeta Rob
și apoi țeava l în / Bună ziua, care va face, de asemenea, același tip de lucru..
Acest lucru este de a lua de ieșire a acestei comenzi, echo Rob,
și folosindu-l ca intrare pentru / salut..
Vă puteți gândi la ea ca redirect primul ecou Rob într-un fișier
și apoi de intrare în / salut. că fișierul care tocmai a fost scoase.
Dar este nevoie de fișier temporar din imagine.
>> Întrebări cu privire la asta?
>> Următoarea întrebare este de gând să implice acest lucru.
Ce ai putea folosi conducta pentru a găsi numărul de nume unice într-un fișier numit names.txt?
Comenzile noi de gând să doriți să utilizați aici sunt unice, atât de Uniq, și apoi WC.
Puteți face Uniq omul să se uite la ceea ce de fapt care face,
și este doar de gând pentru a filtra linii adiacente care se potrivesc de la intrare.
Și omul WC este de gând să imprime cuvântul linie noua,, și numărul de octet pentru fiecare fișier.
Și ultima am de gând să doriți să utilizați un fel,
care este de gând să sortați doar linii de fișier txt.
Dacă aș face un fisier txt, names.txt, și e Rob, Tommy, Iosif, Tommy, Iosif, RJ, Rob,
ceea ce vreau să fac aici este să găsim numărul de nume unice în acest fișier.
Deci, ce ar trebui să fie răspunsul? >> [Elev] 4. Da >>.
Ar trebui să fie 4, deoarece Rob, Tommy, Iosif, RJ sunt numai nume unice în acest fișier.
Primul pas, dacă eu fac doar numărul de cuvinte pe names.txt,
acest lucru este, de fapt spune-mi totul.
Aceasta este de fapt de imprimare - Să vedem, omule WC - rânduri libere, cuvinte, și numărul de octet.
Dacă îmi pasă doar de linii, atunci eu pot face doar wc-am names.txt.
Deci, asta e pasul 1.
Dar eu nu vreau să-l names.txt wc, deoarece conține doar names.txt toate numele,
și vreau să filtreze nici pe cele non-unice.
Deci, dacă am face Uniq names.txt, care nu dă chiar mi ceea ce vreau
deoarece numele duplicate sunt încă acolo.
De ce este asta? De ce nu Uniq face ceea ce vreau?
[Elev] duplicate nu sunt [neauzit] >> Da.
Amintiți-vă pagina de manual pentru Uniq spune filtrul de linii adiacente de potrivire.
Ei nu sunt adiacente, așa că nu le va filtra.
Dacă eu le sorta în primul rând, names.txt sortare se va pune toate liniile duplicat împreună.
Deci, acum names.txt fel este faptul că.
Am de gând să doriți să utilizați că, de intrare la Uniq, care este | Uniq.
Asta îmi dă Iosif, RJ, Rob, Tommy,
și vreau să folosesc ca intrare pentru wc-L,
care este de gând să-mi dea 4.
Ca se spune aici, ce ai putea folosi conducta?
Puteți face o mulțime de lucruri cum ar fi folosind o serie de comenzi
în cazul în care vă folosiți de ieșire dintr-o comandă ca intrare pentru următoarea comandă.
Puteți face o mulțime de lucruri, o mulțime de lucruri inteligente.
>> Întrebări?
Bine.
Asta e. pentru conducte și redirecționarea.
>> Acum mergem pe lucruri reale, chestii de codificare.
În interiorul acestei PDF, veți vedea această comandă,
și veți dori să rulați această comandă în aparatul.
wget este comanda pentru a obține ceva de pe Internet, în esență,
atât de wget și această adresă URL.
Dacă te-ai dus la această adresă URL în browser-ul dumneavoastră, s-ar descărca acest fișier.
Tocmai am dat click pe el, așa că descărcat fișierul pentru mine.
Dar scris wget de chestia aia interiorul terminalului
este doar de gând să-l descărcați în terminalul.
Am section5.zip, și veți dori să dezarhivați section5.zip,
care este de gând să vă dau un folder numit punctului 5,
care va avea toate fișierele care le vom folosi astăzi în interiorul acestuia.
Ca nume de aceste programe "File sugerează, acestea sunt un pic buggy,
astfel încât misiunea ta este să dau seama de ce folosind gdb.
Are toată lumea le-au descărcat / știi cum să-i mai descărcat
în aparat lor? Bine.
>> Rularea ./buggy1, acesta va spune Segmentation fault (core fac obiectul unui dumping),
care în orice moment veți obține o segfault, e un lucru rău.
În ce condiții puteți obține un segfault?
[Elev] Dereferencing un pointer nul. Da >>. Așa că este un exemplu.
Dereferencing un pointer nul ai de gând pentru a obține o segfault.
Ce segfault mijloc este ce te atingi de memorie pe care nu ar trebui să fie ating.
Dereferencing Deci, un pointer nul se atinge adresa 0,
și, practic, toate computerele din zilele noastre spun că 0 este adresa de memorie pe care nu ar trebui să fie ating.
Deci, de aceea dereferencing un rezultat nul indicatorul într-o segfault.
Atunci când se întâmplă, nu pentru a inițializa un pointer, atunci aceasta are o valoare de gunoi,
și așa mai departe, atunci când încercați să-l dereference, după toate probabilitățile, ce te atingi de memorie
că e în mijlocul pustietății.
Dacă se întâmplă să am noroc și valoarea de gunoi
sa întâmplat să indice undeva pe stivă sau ceva,
atunci când dereference că indicatorul care nu ați inițializat,
nimic nu va merge prost.
Dar dacă e îndreptat, să zicem, undeva între stivă și heap,
sau este îndreptat doar pentru undeva că nu a fost folosit de programul tău încă,
atunci esti atingi de memorie pe care nu ar trebui să fie atingerea și tu segfault.
Când scrieți o funcție recursivă și recurses de prea multe ori
și stack-ul creste prea mare și se loveste stivei în lucruri
că nu ar trebui să fie coliziunea cu, te atingi de memorie pe care nu ar trebui să fie atingerea,
astfel încât să segfault.
Asta este ceea ce o segfault este.
>> Este, de asemenea, din același motiv că, dacă aveți un șir cum ar fi -
hai să ne întoarcem la programul anterior.
În hello.c--Sunt doar de gând să facă altceva.
char * s = "Hello World!";
Dacă am folosi * s = ceva sau s [0] = "X";
asa ca salut, / Buna ziua., de ce a făcut asta segfault?
De ce a făcut asta segfault?
Ce v-ar aștepta să se întâmple?
Dacă am făcut printf ("% s \ n", s); ceea ce ați aștepta să fie tipărite?
[Elev] X salut. Da >>.
Problema este că, atunci când declară un șir de acest fel,
s este un pointer care este de gând să meargă în stivă,
și ceea ce s indică spre este acest șir de care este conținută în memorie read-only.
Deci, doar de nume, memorie read-only, trebuie să obțineți o idee
că, dacă încercați să schimbați ceea ce e în memorie read-only,
faci ceva ce nu ar trebui să faci și tu cu memorie segfault.
Aceasta este de fapt o mare diferenta intre char * s și s char [].
Deci, char s [], acum acest șir va fi pus pe stivă,
și stiva nu este doar în citire, ceea ce înseamnă că ar trebui să funcționeze perfect această regulă.
Și-l face.
Amintiți-vă că, atunci când fac char * s = "Hello World!", E în sine este pe stiva
dar subliniază s în altă parte, și că în altă parte se întâmplă să fie doar în citire.
Dar char s [] este doar ceva pe stiva.
Deci, asta e un alt exemplu de segfault întâmplă.
>> Am văzut că ./buggy1 a dus la o segfault.
În teorie, nu ar trebui să se uite la buggy1.c imediat.
În schimb, ne vom uita la ea prin gdb.
Observați că atunci când vei ajunge Segmentation fault (core fac obiectul unui dumping),
te acest fișier peste miezul aici sunat.
Dacă ne ls-l, vom vedea că de bază este, de obicei, un fișier destul de mare.
Acesta este numărul de octeți din dosar, astfel încât se pare că e ceva 250-kilobytes.
Motivul pentru aceasta este că ceea ce haldei de bază este de fapt
este atunci când dumneavoastră program se blochează, starea de memorie a programului
doar se copiate și lipite în acest fișier.
Ea devine aruncat în acel fișier.
Acest program, în timp ce fugea, sa întâmplat să aibă o memorie usage de aproximativ 250 kilobiți,
și pentru ca e ceea ce a fost aruncat în acest fișier.
Acum poti sa te uiti la acel fișier, dacă facem gdb de bază buggy1.
Putem face doar gdb buggy1, și că va începe doar până gdb în mod regulat,
folosind buggy1 ca fișier de intrare său.
Dar dacă ai face gdb de bază buggy1, apoi se va în mod special pentru a porni gdb
uitandu-se la faptul că fișierul de bază.
Și tu spui buggy1 gdb înseamnă știe că fișierul de bază vine de la programul de buggy1.
Deci, gdb buggy1 de bază se va aduce imediat ne
pentru a în cazul în care programul sa întâmplat de a rezilia.
Vedem aici programul încheiată cu semnal 11, Segmentation fault.
Se întâmplă să vedem o linie de asamblare, care, probabil, nu este de foarte mare ajutor.
Dar, dacă tastați BT sau backtrace, care va fi funcția de
care ne dă lista de cadre noastre actuale stivă.
Deci, backtrace. Se pare că avem doar două cadre stack.
Primul este cadrul nostru principal stivă,
iar al doilea este cadru stivă pentru această funcție, care se întâmplă să fie în,
care arata ca avem doar cod de asamblare pentru.
Așa că hai să mergem înapoi în funcție nostru principal,
și pentru a face ca putem face cadru 1, și cred că putem face, de asemenea, în jos,
dar eu niciodată nu fac aproape în jos - sau în sus. Da.
În sus și în jos. Până va aduce la un cadru stivă, care se va aduce în jos un cadru stivă.
Eu tind să nu utilizeze niciodată asta.
Eu doar spun în mod specific cadru 1, care se trece la cadrul etichetat 1.
Cadrul 1 este de gând să ne aducă în cadrul stivă principal,
și se spune chiar aici linie de cod se întâmplă să fie la.
Dacă ne-am dorit un cuplu mai multe linii de cod, putem spune listă,
și care va să ne dea toate liniile de cod în jurul ei.
Linia ne segfaulted a fost de 6 la:
în cazul în care (strcmp ("CS50 pietre", argv [1]) == 0).
În cazul în care nu este evident încă, puteți să-l direct de aici doar de gândesc de ce-l segfaulted.
Dar ne putem lua un pas mai departe și spune, "De ce ar argv [1] segfault?"
Imprimare Să argv [1], si se pare ca e 0x0, care este pointer nul.
Suntem strcmping CS50 pietre și nule, și astfel încât va segfault.
Și de ce este argv [1] nul?
[Elev] Pentru ca nu am da orice argumente de linie de comandă.
Da. Noi nu am da orice argumente de linie de comandă.
Deci, ./buggy1 este doar de gând să aibă argv [0] este ./buggy1.
Ea nu va avea un argv [1], astfel încât va segfault.
Dar dacă, în schimb, să fac doar CS50, o să spun Veți obține un D
pentru că asta e ceea ce ar trebui sa fac.
Privind la buggy1.c, se presupune pentru a imprima "Veți obține un D" -
Dacă argv [1] nu este "CS50 pietre", "Ai un D", altfel "Veți obține un A!"
Deci, dacă vrem un A, avem nevoie de aceasta pentru a compara ca fiind adevărate,
ceea ce înseamnă că o compară cu 0.
Deci, argv [1] trebuie să fie "pietre" CS50.
Dacă vrei să faci asta pe linia de comandă, aveți nevoie pentru a utiliza \ pentru a scăpa spațiu.
Deci CS50 \ roci și veți obține un A!
Dacă nu faci backslash, de ce nu funcționează asta?
[Elev] E două argumente diferite. Da >>.
Argv [1] va fi CS50, și argv [2] va fi pietre. Bine.
>> Acum ./buggy2 este de gând să segfault din nou.
În loc de ao deschide cu fișierul miezul ei, vom deschide doar până buggy2 direct,
așa gdb buggy2.
Acum, dacă vom rula doar programul nostru, atunci o să spun Programul a primit semnal de SIGSEGV,
care este segfault semnal, și acest lucru este în cazul în care sa întâmplat să se întâmple.
Privind la backtrace nostru, vom vedea că am fost în oh_no funcție,
care a fost numit de catre Dinky funcția, care a fost numit de către Binky funcția,
care a fost numit de către principal.
Putem vedea, de asemenea, argumentele pentru aceste funcții.
Argument pentru Dinky și Binky a fost de 1.
Dacă am lista functia oh_no, vom vedea că oh_no este de a face doar char ** s = NULL;
* S = "boom";
De ce ar eșua asta?
[Elev] Nu poți dereference indicatorul nul? Da >>.
Acest lucru este doar că e este NULL, indiferent în cazul în care se întâmplă să fie un char **,
care, în funcție de modul în care interpreta, ar putea fi un pointer la un pointer la un șir de
sau o serie de siruri de caractere.
E s este NULL, deci * s este un pointer nul dereferencing,
și astfel aceasta se va prabusi.
Aceasta este una dintre cele mai rapide moduri în care puteți segfault, eventual.
E doar o declarare pointer nul și imediat segfaulting.
Asta e ceea ce face oh_no.
Dacă vom merge în sus cu un cadru, apoi vom intra în funcția de care a sunat oh_no.
Am nevoie să fac asta în jos.
Dacă nu introduceți o comandă și te-a lovit doar Enter din nou,
se va repeta doar comanda anterioară pe care ai fugit.
Suntem în cadru 1.
Listarea acest cadru, vom vedea aici este funcția noastră.
Puteți lovi din nou lista, sau poti face lista 20 și se va lista mai mult.
Dinky Funcția spune dacă i este 1, atunci du-te la funcția de oh_no,
du-te la altceva funcția Slinky.
Și știm i este 1, deoarece se întâmplă să vedem aici
Dinky că a fost numit cu argumentul 1.
Sau poți să faci pur și simplu i imprima și se va spune i este 1.
Suntem în prezent în Dinky, și dacă mergem un alt cadru, noi știm că vom ajunge în Binky.
Sus. Acum suntem în Binky.
Listarea această funcție - lista de tăiat jumătate înainte de mine off -
Totul a început ca în cazul în care i este 0, atunci vom numi oh_no, suna altfel Dinky.
Stim I a fost de 1, așa că numita Dinky.
Și acum ne-am întors în principal, și principal este doar de gând să fie int i = rand ()% 3;
Asta este doar de gând să vă dau un număr aleatoriu care este fie 0, 1, sau 2.
Se va numi Binky cu acel număr, și se va întoarce 0.
Privind la acest lucru,
doar de mers pe jos prin programul manual fără a executa imediat,
v-ar stabili un punct principal de pauza de la, ceea ce înseamnă că, atunci când vom rula programul
programul rulează până când se lovește un punct de spargere.
Deci, rularea programului, acesta va rula și apoi va lovi funcția principală și se va opri de rulare.
Acum suntem în interiorul principal, și pasul următor este sau ne va aduce la următoarea linie de cod.
Puteți face pasul următor sau.
Lovirea următor, acum i-a fost setat la rand ()% 3, astfel încât să putem tipări valoarea lui I,
și-l va spune i este 1.
Acum contează dacă vom folosi următoarea etapă sau.
Cred că a contat în cel precedent, dar ne-am dori să utilizați următoarea.
Dacă vom folosi pas, vom păși în funcție, ceea ce înseamnă gasiti lucru real
ce se intampla in interiorul Binky.
Dacă vom folosi urmatoarea, atunci înseamnă că trece peste funcția
și du-te la următoarea linie de cod în funcție nostru principal.
Chiar aici pe această linie, în cazul în care am fost la ea a spus RAND () de 3%;
dacă am făcut pas, ar intra în punerea în aplicare a rand
si uita-te la ceea ce se intampla acolo, si am putut parcurge funcția de rand.
Dar nu-mi pasă de funcția rand.
Vreau doar să merg la următoarea linie de cod, în principal, așa că am folosi următoarea.
Dar acum îmi pasă de funcția Binky, așa că vreau să pășească în asta.
Acum sunt în Binky.
Prima linie de cod se va spune în cazul în care (i == 0), iau un pas,
vom vedea vom ajunge la Dinky.
Dacă noi lucrurile lista, vedem că acesta este verificat i = 0.
i nu este egal cu 0, așa că a mers la starea altceva,
care este de gând să sun Dinky (i).
S-ar putea obține confuz.
Dacă te uiți doar la aceste linii direct, ai putea crede că, dacă (i == 0),
ok, apoi am luat un pas, iar acum eu sunt la Dinky (i),
s-ar putea crede că trebuie să însemne i = 0 sau ceva.
Nu, înseamnă doar că se știe că se poate lipi direct la linia de Dinky (i).
Pentru că nu este 0, pasul următor nu se va termina la altceva.
Altceva nu este o linie se va opri de la.
E doar de gând să meargă la următoarea linie se poate executa de fapt, care este Dinky (i).
Pășind în Dinky (i), vom vedea dacă (i == 1).
Știm i = 1, asa ca atunci cand pasim, știm că vom ajunge în oh_no
deoarece i = 1 solicită oh_no funcția, pe care aveți posibilitatea să pas în,
care este de gând să setați char ** s = NULL și imediat la "boom".
Și apoi, de fapt se uită la punerea în aplicare a buggy2,
acest lucru, i se obtinerea doar un număr aleatoriu - 0, 1, sau 2 - chemare Binky,
care, dacă i este 0 se numește oh_no, altfel solicită Dinky, care vine aici.
Dacă i este 1, apelul oh_no, suna altfel Slinky, care vine aici,
în cazul în care i este 2, suna oh_no.
Nu cred că chiar și acolo este o modalitate -
Are cineva vedea o modalitate de a face acest lucru un program care nu va segfault?
Pentru că dacă nu mă lipsește ceva, dacă i este 0, vei segfault imediat,
altceva te duci la o funcție care, dacă i este 1 vă segfault,
altceva te duci la o funcție în cazul în care, dacă i este 2 vă segfault.
Deci, indiferent de ceea ce faci, te segfault.
>> Cred ca o modalitate de a stabili, ar fi loc de a face char ** s = NULL,
ai putea malloc spațiu pentru acel șir.
Am putea face malloc (sizeof) - sizeof ce?
[Elev] (char) * 5? Are acest >> se pare corect?
Sunt presupunând că acest lucru va funcționa dacă eu de fapt ea a fugit, dar nu e ceea ce caut.
Uită-te la tipul de e. Să adăugăm * int, int * x asa.
Mi-ar face malloc (sizeof (int)).
Sau dacă am vrut o serie de 5, mi-ar face (sizeof (int) * 5);
Ce se întâmplă dacă am o int **?
Ce aș malloc?
[Elev] Mărimea indicatorului. Da >>. (Sizeof (int *));
Acelasi lucru aici.
Vreau (sizeof (char *));
Acest lucru se întâmplă pentru a aloca spațiu pentru indicatorul care indică "boom".
Nu am nevoie să aloce spatiu pentru "boom" în sine
deoarece aceasta este de fapt echivalent cu ceea ce am spus mai înainte
de char * x = "boom".
"Boom" există deja. Se întâmplă să existe în regiunea read-only de memorie.
Dar ea există deja, ceea ce înseamnă această linie de cod, în cazul în care s este un char **,
* atunci s este un char * si tu setarea acestei char * pentru a indica "boom".
Dacă aș fi vrut să copiați "boom" în s, atunci mi-ar trebui să aloce spațiu pentru s..
Voi face * s = malloc (sizeof (char) * 5);
De ce 5?
De ce nu 4? Se pare ca "boom" este de 4 caractere. >> [Elev] caracterul nul.
Da. Toate siruri de caractere dvs. sunt de gând să nevoie de un caracter nul.
Acum pot face ceva de genul strcat - Care este funcția pentru copierea unui șir?
[Elev] cpy? Strcpy >>.
strcpy om.
Deci, strcpy sau strncpy.
strncpy este un pic mai sigur, deoarece aveți posibilitatea să specificați exact câte caractere,
dar aici nu contează pentru că știm.
Deci, strcpy si uita-te la argumentele.
Primul argument este destinația noastră.
Al doilea argument este sursa noastră.
Vom copia în * destinația noastră e indicatorul "boom".
De ce ar putea să vă doriți să faceți acest lucru cu un strcpy în loc de doar ceea ce am avut înainte de
de * s = "boom"?
Există un motiv pentru care s-ar putea dori să faceți acest lucru, dar ceea ce este aceea?
[Elev] Dacă doriți să schimbați ceva în "boom". Da >>.
Acum pot face ceva de genul s [0] = "X";
deoarece punctele s la grămadă și că spațiul pe heap, care s indică spre
este un pointer la mai mult spatiu pe heap, care este stocarea "boom".
Deci, această copie a "boom" este a fi depozitate în grămadă.
Există două copii ale punct de vedere tehnic "boom" în programul nostru.
Nu e primul care e doar dat de prezenta constanta șir "boom",
și al doilea exemplar al "boom", strcpy a creat copie a "boom".
Dar copie a "boom" se stocate pe heap, iar heap ești liber să se schimbe.
Heap nu este doar în citire, astfel încât înseamnă că e [0]
este de gând să vă schimbați valoarea "boom".
O să vă permit să modificați aceste caractere.
>> Întrebări?
Bine.
>> Trecând la buggy3, haideți să gdb buggy3.
Am doar rulați-l și vedem vom obține o segfault.
Dacă vom backtrace, există doar două funcții.
Dacă vom merge în sus, în funcție de principalul nostru, vom vedea că am segfaulted la această linie.
Deci, doar se uită la această linie, pentru (int = 0; line fgets astea nu NULL este egal;
Linia + +).
Cadru noastră anterioară a fost numit _IO_fgets.
Veți vedea că o mulțime cu built-in functii C,
că atunci când vei ajunge segfault, nu va fi într-adevăr numele criptice de funcții
Ca acest _IO_fgets.
Dar asta se va referi la acest apel fgets.
Undeva în interiorul aici, suntem segfaulting.
Dacă ne uităm la argumentele la fgets, putem imprima tampon.
Să o imprimați ca - Oh, nu.
Imprimare nu este de gând să lucreze exact așa cum vreau să.
Să ne uităm la programul actual.
Buffer-ul este un sir de caractere. E un sir de caractere de 128 de caractere.
Deci, atunci când spun tampon de imprimare, se va imprima aceste 128 de caractere,
care cred că este ceea ce este de așteptat.
Ceea ce am fost cautati pentru este imprima adresa de tampon,
dar asta nu ma spune mult.
Asa ca atunci cand se intampla sa spun aici x tampon, se arată-mi 0xbffff090,
care, dacă vă amintiți de la mai devreme sau un anumit punct, Oxbffff tinde să fie o regiune stack-ish.
Stivă tinde să începem de undeva doar sub 0xc000.
Doar prin văzând această adresă, știu că buffer-ul se întâmplă pe stiva.
Repornirea programul meu, fugi, te, tampon am vazut a fost această secvență de caractere
care sunt destul de mult sens.
Apoi imprimați fișierul, ceea ce nu arata dosar?
[Elev] Null. Da >>.
Fișier este un tip de * FILE, deci este un pointer,
și valoarea pe care indicatorul este nul.
Deci, fgets este de gând să încerce să citească de la faptul că indicatorul într-un mod indirect,
dar pentru a accesa că indicatorul, trebuie să-l dereference.
Sau, pentru a accesa ceea ce ar trebui să fie îndreptată spre, îl dereferences.
Deci, este un pointer nul dereferencing și-l segfaults.
Aș fi putut să-l reluat acolo.
Dacă ne rupe la punctul nostru principal și a alerga,
prima linie de cod este char * filename = "nonexistent.txt";
Asta ar trebui să dea un indiciu destul de mare ca de ce acest program nu reușește.
Tastarea lângă mine aduce la linia următoare, în cazul în care am deschis acest fișier,
și apoi mă imediat în linia noastră, în cazul în care o dată m-am lovit următoare, o să segfault.
Vrea cineva să arunce un motiv pentru care ar putea fi segfaulting?
[Elev] Fișierul nu există. Da >>.
Acest lucru ar trebui să fie un indiciu
că ori de câte ori sunteți deschiderea unui fișier care aveți nevoie pentru a verifica dacă fișierul există de fapt.
Deci, aici, "nonexistent.txt";
Când ne-am Filename fopen pentru lectură, avem atunci trebuie să spunem
în cazul în care (fișier == NULL), și spun printf ("Fișierul nu există!"
sau - mai bine - filename); întoarcere 1;
Deci, acum vom verifica pentru a vedea dacă este NULL
înainte de a continua de fapt, și încercarea de a citi din acel fișier.
Ne putem reface doar pentru a vedea că lucrările.
Mi-am propus să includă o linie nouă.
Deci, acum nonexistent.txt nu există.
Trebuie să verificați întotdeauna pentru acest fel de lucruri.
Trebuie să verificați întotdeauna pentru a vedea dacă fopen returnează NULL.
Trebuie să verificați întotdeauna pentru a vă asigura că malloc nu se întoarce NULL,
sau altceva segfault.
>> Acum buggy4.c.
Rularea. Cred că acest lucru este în așteptare pentru intrare sau, eventual, loop infinit.
Da, e infinită looping.
Deci, buggy4. Se pare ca suntem looping infinit.
Ne putem rupe de la principal, executați programul nostru.
În gdb, atâta timp cât utilizați abrevierea este lipsit de ambiguitate
sau abrevieri speciale care le oferă pentru tine,
le puteti folosi pentru a utiliza următoarea n loc de a avea pentru a tasta următoare tot drumul.
Și acum că am lovit o dată n, eu pot doar să apăsați Enter pentru a continua următoare
în loc de a avea pentru a lovi n Introduceți, n Introduceți, n Enter.
Se pare că sunt într-un fel de buclă pentru care este setarea matrice [i] la 0.
Se pare ca n-am ieși din această buclă pentru.
Dacă eu am imprima, astfel încât i este 2, atunci voi merge în continuare.
Voi i imprima, i este 3, atunci voi merge în continuare.
Voi i imprima și i este 3. În continuare, imprima i, i este 4.
De fapt, de imprimare sizeof (array), astfel încât dimensiunea matrice este 20.
Dar se pare ca nu e ceva de comandă specială gdb pentru a merge până la ceva se întâmplă.
E ca și cum o condiție privind stabilirea valorii variabilei. Dar eu nu-mi amintesc ce este.
Deci, dacă vom continua -
Ce spuneai? Ce ai adus sus?
[Elev] se afișează adaug - >> Da. Deci afișa te pot ajuta.
Dacă vom afișa doar i, se va pune aici ce valoarea i este
așa că nu trebuie să-l imprimați fiecare dată.
Dacă ținem doar de gând următoare, vom vedea 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5.
Ceva se întâmplă teribil de greșit, și i se resetat la 0.
Privind la buggy4.c, vom vedea tot ce se întâmplă este matrice int [5];
pentru (i = 0; i <= sizeof (array); i + +)
matrice [i] = 0;
Ce vom vedea că e în neregulă aici?
Ca un indiciu, atunci când am fost făcut gdb buggy4 - hai rupe termen principal, -
Am facut print sizeof (array) doar pentru a vedea ce condiție este locul unde ar trebui să rup în cele din urmă.
În cazul în care sunt eu? Credeți alerg?
Nu am declara încă.
Imprima astfel încât sizeof (array) și că e 20,
care este de așteptat, deoarece gama mea este de marimea 5 și este de 5 numere întregi,
astfel încât întregul lucru ar trebui să fie de 5 * sizeof (int) octeți, în cazul în care sizeof (int) tinde să fie 4.
Deci, sizeof (array) este de 20.
Ce ar trebui să fie asta?
[Elev] Împărțit de sizeof (int). Da >>, / sizeof (int).
Se pare că există încă o problemă aici. Cred că acest lucru ar trebui să fie doar <
din moment ce este destul de mult tot timpul <niciodată și <=.
Acum, să ne gândim de ce acest lucru a fost, de fapt rupt.
Are cineva ghiceste de ce am fost resetat la 0 prin fiecare iterație a buclei?
Singurul lucru de aici în interiorul ce se intampla este ca matrice [i] este în curs de la 0.
Deci într-un fel, această linie de cod este cauza noastră int i pentru a fi setat la 0.
[Elev] Ar putea fi, deoarece este imperative de memorie din această parte a I
atunci când crede că e urmatorul element al array? >> [Bowden] Da.
Când mergem dincolo de sfârșitul anului matrice noastre,
într-un fel că spațiul pe care suntem imperative este imperative valoarea lui i.
Și astfel, dacă ne uităm în buggy4, rupe alerga principal,,
să imprimați adresa lui i.
Se pare ca o bffff124.
Acum, haideți să imprimați adresa de matrice [0]. 110.
Ce zici [1]? 114.
[2], 118.
11c, 120. matrice [5] este bfff124.
Deci, matrice [5] are aceeași adresă ca i, ceea ce înseamnă că matrice [5] este i.
Dacă au aceeași adresă, ele sunt același lucru.
Așa că atunci când ne-am stabilit matrice [5] la 0, suntem i setarea la 0.
Și dacă te gândești la asta în termeni de stivă,
int i este declarată în primul rând, ceea ce înseamnă că devine ceva spațiu pe stivă.
Apoi matrice [5] este alocată, astfel încât atunci 20 bytes sunt alocate pe stivă.
Asa ca am devine alocate în primul rând, atunci aceste 20 bytes te alocate.
Asa ca am se întâmplă chiar înainte de matrice,
și din cauza modului, cum am spus săptămâna trecută, în cazul în care punct de vedere tehnic stiva creste în jos,
Atunci când indicele într-o matrice, ne sunt garantate că poziția 0th în matrice
Întotdeauna se întâmplă înainte de prima pozitie in matrice.
Aceasta este un fel de modul în care l-am desenat săptămâna trecută.
Observați că în partea de jos avem adresa 0 și la partea de sus avem max adresa.
Stiva este întotdeauna în creștere în jos.
Să spunem că am aloca.
Noi aloca integer i, ceea ce înseamnă că hai să spunem doar aici întreg i se alocă.
Apoi ne-am aloca gama noastră din 5 numere întregi, ceea ce înseamnă că sub că,
deoarece stiva este în creștere în jos, cele 5 numere întregi se alocate.
Dar, din cauza modului matrice funcționează, suntem garantat că prima poziție în matrice
are întotdeauna o adresă mai mică de al doilea lucru în matrice.
Deci, poziția 0 matrice întotdeauna trebuie să se întâmple mai întâi în memorie,
întrucât poziția matrice 1 are să se întâmple după aceea
și poziția matrice 2 are să se întâmple după aceea,
ceea ce înseamnă că poziția 0 matrice s-ar întâmpla undeva aici jos,
Poziția matrice 1 de mai sus s-ar întâmpla asta
deoarece se deplasează în sus înseamnă adrese mai mari, deoarece adresa maximă este de până aici.
Deci, matrice [0] aici jos, matrice [1] până aici, matrice [2] până aici, matrice [3] aici.
Înainte de a observa cum am alocat întreg i tot drumul până aici,
asa cum am merge mai departe și mai departe în gama noastră, suntem tot mai aproape de a noastră întreg i.
Pur și simplu așa se întâmplă că matrice [5], care este o poziție dincolo de gama noastră,
este exact în cazul în care întreg i sa întâmplat să fie alocate.
Deci, asta e punctul în care se întâmplă să fie lovit spațiul de pe stivă
care a fost alocat pentru întreg i, și suntem setarea că la 0.
>> Asta e modul în care funcționează. Întrebări? Da.
[Elev] Nu contează. Bine.
[Elev] Cum puteți evita aceste tipuri de erori?
Acestea fel de erori? Nu folosiți limbaj de programare C, după dumneavoastră.
Folositi un limbaj care are limite de matrice de control.
Atâta timp cât ești atent, trebuie doar sa evite sa mearga dincolo de limitele matrice dumneavoastră.
[Elev] Deci, aici, când ne-am dus dincolo de limitele matrice dumneavoastră -
[Bowden] Asta e în cazul în care lucrurile încep sa mearga prost. >> [Elev] Oh, bine.
Atâta timp cât stai în memorie alocată pentru matrice dvs., ești bine.
Dar C nu face verificarea erorilor. Dacă fac matrice [1000], acesta va modifica doar cu bucurie ceea ce se întâmplă -
Se merge la începutul matrice, apoi merge pozitii dupa 1000 si il seteaza pe 0.
Ea nu face nici o verificare că oh, acest lucru nu are de fapt 1000 de lucruri în ea.
1000 este dincolo de ceea ce ar trebui să se schimbe,
întrucât Java sau ceva veți obține gama de indicele de limitele
sau indicele de excepție limitele.
De aceea, o mulțime de limbi de nivel superior au aceste lucruri
în cazul în care dacă te duci dincolo de limitele matrice, nu reușesc
astfel încât să nu se poate schimba lucrurile de la sub tine
și apoi lucrurile merg mult mai rău decât obținerea pur și simplu o excepție
spunând că te-ai dus dincolo de sfârșitul matrice.
[Elev] Și astfel ar trebui să ne-am schimbat doar <= la doar <? >> [Bowden] Da.
Ar trebui să fie <sizeof (array) / sizeof (int);
deoarece sizeof (array) este de 20, dar vrem doar 5. >> [Elev] Corect.
Mai multe întrebări? Bine.
>> [Elev] Am o întrebare. Da >>.
[Elev] Care este variabila matrice reală?
[Bowden] Ca ce este matrice?
Array sine este un simbol.
Acesta este doar adresa de start a 20 bytes pe care le fac referire.
Vă puteți gândi la ea ca la un pointer, dar este un pointer constant.
De îndată ce lucrurile se compilate, matrice variabilă nu mai există.
[Elev] Deci, cum se afla dimensiunea matrice?
Mărimea matrice se referă la dimensiunea de care blocheaza că simbolul se referă la.
Când m-am face ceva de genul printf ("% p \ n", array);
hai să-l rulați.
Ce-am făcut greșit?
"Matrice" matrice a declarat aici.
Oh, aici.
Zăngănit este inteligent, și se întâmplă să observați că am declarat ca matrice 5 elemente
dar eu sunt indexarea în poziția 1000.
Se poate face acest lucru, deoarece acestea sunt doar constante.
Se poate merge numai în măsura în observ că am de gând dincolo de limitele matrice.
Dar, înainte de a observa când am avut am fi incorectă,
nu se poate determina, eventual, câte valori i-ar putea lua pe,
astfel încât nu se poate stabili faptul că am fost de gând dincolo de sfârșitul matrice.
Asta e doar zăngănit fiind inteligent.
>> Dar fac acum buggy4. Deci, ce altceva fac gresit?
Implicit, declarând funcția de bibliotecă "printf".
Am de gând să doriți să includeți # .
Bine. Rulează acum buggy4.
Imprimarea valoarea de matrice așa cum am făcut-o aici, tipărirea ca un pointer
printuri ceva care arata ca acest lucru - bfb8805c - ceea ce este o adresa
care este în regiunea stiva-ish.
Array sine este ca un pointer, dar nu este un indicator real,
deoarece un pointer regulat putem schimba.
Array este doar o constantă. Cele 20 de blocuri de memorie încep de la 0xbfb8805c adresa.
Deci bfb8805c prin această adresă +20- sau cred -20 -
este tot din memorie alocată pentru această matrice.
Array, variabila în sine nu este stocat nicăieri.
Când sunteți compilarea, compilatorul - val mâna la ea -
dar compilatorul va folosi doar în cazul în care se știe matrice să fie.
Se știe că în cazul în care începe matrice,
si asa se poate face doar lucrurile întotdeauna în termeni de compensări de la acest început.
Ea nu are nevoie de o variabilă sine pentru a reprezenta matrice.
Dar când fac ceva de genul int * p = array; acum p este un pointer care indică faptul că matrice,
și acum, de fapt p exista pe stiva.
Sunt liber să schimbe p.. Eu pot face p = malloc.
Așa că inițial a subliniat matrice, acum arată la unele spațiu pe heap.
Eu nu pot face matrice malloc =.
Dacă zăngănit este inteligent, el va țipa la mine right off BAT.
De fapt, eu sunt destul de sigur că gcc-ar face acest lucru prea.
Deci, de tip array "int [5]" nu este transmisibile.
Nu puteți atribui ceva la un tip de matrice
deoarece matrice este doar o constantă.
Este un simbol care trimiterile cele 20 de octeți. Eu nu pot schimba.
>> [Elev] Iar în cazul în care este dimensiunea matrice stocate?
[Bowden] Nu este stocat nicăieri. E atunci când este compilarea.
Deci, în cazul în care este dimensiunea matrice stocate?
Puteți utiliza numai sizeof (array) în interiorul funcției, care este ea însăși matrice declarată.
Deci, dacă am face o anumită funcție, foo, și îmi place (int matrice [])
printf ("% d \ n", sizeof (array));
și apoi în jos aici, eu numesc foo (matrice);
în interiorul acestei funcții - să-l rulați.
Acest lucru este zăngănit fi inteligent din nou.
Este să spui că sizeof pe parametrul funcției matrice
va reveni dimensiunea "* int".
Aceasta ar fi o eroare dacă nu e ceea ce am vrut să se întâmple.
Să ne îndreptăm, de fapt off Werror.
Avertisment. Avertismente sunt bine.
Acesta va compila în continuare atâta timp cât acesta are un avertisment.
. / A.out este de gând să imprima 4.
Avertizare care a fost generat este un indicator clar a ceea ce nu a mers bine.
Această matrice int este doar de gând să imprima sizeof (int *).
Chiar daca am pus matrice [5] aici, este încă doar de gând să imprima sizeof (int *).
Asa ca imediat ce-l treci într-o funcție, distincția între matrice și pointeri
este inexistent.
Acest lucru se întâmplă să fie o matrice care a fost declarată în stivă,
dar de îndată ce trecem această valoare, care 0xbf bla, bla, bla în această funcție,
atunci acest indicator arată faptul că pentru a matrice pe stiva.
Deci asta înseamnă că sizeof se aplică numai în funcția pe care a fost declarată matrice,
ceea ce înseamnă că, atunci când sunt compilarea această funcție,
atunci când face să răsune trece prin această funcție, el vede matrice este o matrice de int marimea 5.
Deci, atunci se vede sizeof (array). Ei bine, asta e 20.
Asta e de fapt modul în care funcționează practic sizeof pentru aproape toate cazurile.
Sizeof nu este o funcție, e un operator.
Tu nu apela funcția sizeof.
Sizeof (int), compilatorul va traduce doar că la 4.
Ai inteles? Bine.
>> [Elev] Deci, ceea ce este diferența dintre sizeof (array), în principal și în foo?
Acest lucru se datorează faptului că noi spunem sizeof (array), care este de tipul int *,
întrucât matrice aici nu este de tip int *, este o matrice int.
>> [Elev] Deci, dacă ați avut parametrul în matrice [] în loc de matrice * int,
ar însemna că că ați fi putut schimba în continuare matrice, deoarece acum este un pointer?
[Bowden] Ca asta? >> [Elev] Da. Poți schimba matrice în funcția de acum?
[Bowden] Ai putea schimba matrice, în ambele cazuri.
În ambele cazuri, sunteți liberi să spună matrice [4] = 0.
[Elev] Dar vă pot face punctul de matrice la altceva?
[Bowden] Oh. Da. În ambele cazuri - >> [elev] Da.
[Bowden] Distincția între matrice [] și o matrice * int, nu este nici unul.
Puteți obține de asemenea, unele matrice multidimensională aici
pentru unii sintaxa convenabil, dar este încă doar un pointer.
Acest lucru înseamnă că sunt liber să fac array = malloc (sizeof (int)); iar acum punctul în altă parte.
Dar la fel ca și modul în care aceasta funcționează pentru totdeauna și întotdeauna,
modificarea acestei matrice, făcând-o să arate altceva
nu schimbă această matrice aici pentru că este o copie a argumentului,
nu este un pointer la acest argument.
Și, de fapt, la fel ca și indicație mai mult că e exact la fel -
am vazut deja ce printuri de imprimare matrice -
Ce se întâmplă dacă am imprima adresa matrice sau adresa adresa matrice
la oricare dintre aceste?
Să ignorăm asta.
Bine. Acest lucru este bine. Se rulează acum / a.out..
Matrice de imprimare, apoi imprimarea adresa matrice, sunt același lucru.
Matrice pur și simplu nu există.
Ea știe când sunteți de imprimare matrice, sunteți de imprimare simbol care face referire la cele 20 de octeți.
Imprimarea adresa matrice, ei bine, matrice nu există.
Ea nu are o adresă, așa că imprimă doar adresa de cele 20 de octeți.
De îndată ce vă compilați jos, ca în buggy4 ta compilat / a.out.,
matrice este inexistent.
Pointeri există. Matrice nu.
Blocurile de memorie care reprezintă matrice există încă,
dar matrice variabilă, iar variabilele de acest tip nu există.
Acestea sunt la fel ca principalele diferențe dintre tablouri și pointeri
sunt la fel de repede ca tine face apeluri de funcții, nu există nici o diferență.
Dar în interiorul funcției pe care matrice în sine este declarată, sizeof funcționează diferit
din moment ce te imprimarea dimensiunea de blocuri în loc de mărimea tipul,
și nu se poate schimba pentru că este un simbol.
Imprimarea lucru și adresa lucru imprimă același lucru.
Și asta e destul de mult.
[Elev] Poți spune că o dată mai mult?
S-ar putea să fi scăpat ceva.
Matrice de imprimare și adresa matrice imprimă același lucru,
întrucât dacă imprimați un pointer comparativ cu adresa pointer,
singurul lucru imprimă adresa a ceea ce indică spre,
celălalt imprimă adresa indicatorul pe stivă.
Aveți posibilitatea să modificați un pointer, nu se poate schimba un simbol matrice.
Și indicatorul sizeof este de gând să imprima dimensiunea de acest tip pointer.
Deci, int * p sizeof (p) este de gând să imprima 4,
dar int matrice [5] print sizeof (array) este de gând să imprima 20.
[Elev] Deci, int matrice [5] se vor imprima 20? Da >>.
De aceea, în interiorul buggy4 atunci când este folosit pentru a fi sizeof (array)
acest lucru a fost făcut eu <20, care nu este ceea ce ne-am dorit.
Ne dorim ca i <5. >> [Elev] Ok.
[Bowden] Și apoi, cât mai curând începe să paseze în funcțiile,
dacă am făcut-o int * p = array;
în interiorul acestei funcții, putem folosi practic p și matrice în exact aceleași moduri,
cu excepția problemei sizeof și problema schimbare.
Dar p [0] = 1; este aceeași cu a spune matrice [0] = 1;
Și de îndată ce am spus foo (array) sau foo (p);
interiorul funcției foo, aceasta este aceeași chemare de două ori.
Nu există nici o diferență între aceste două apeluri.
>> Bun pe care toată lumea? Bine.
Avem 10 minute.
>> Vom încerca să obțineți prin intermediul acestui program de Typer Hacker,
acest site, care a venit în anul trecut sau ceva de genul.
Este doar ar trebui să fie ca tine tastați aleatoriu și se imprimă -
Orice fișier se întâmplă să fi încărcat este ceea ce se pare că îl tastați.
Se pare ca un fel de cod sistem de operare.
Asta e ceea ce vrem să pună în aplicare.
Tu ar trebui să aibă un executabil binar numit hacker_typer
care ia într-un singur argument, fișierul "de tip hacker."
Rularea executabil ar trebui să goli ecranul
și apoi să imprimați afară de un caracter din fișierul pass-in de fiecare dată când utilizatorul apasă o cheie.
Deci, indiferent de ce apăsați tasta, ar trebui să arunce și să imprimați în schimb un personaj din dosar
că este argumentul.
Voi destul de mult ti spun ce lucrurile pe care le vom avea nevoie să știe sunt.
Dar vrem să verificați bibliotecă termios.
Nu am folosit această bibliotecă în toată viața mea, așa că are scopuri foarte minime.
Dar acest lucru se va fi bibliotecă putem folosi pentru a arunca caracterul te-a lovit
atunci când sunt tastând în iarnă inch
Deci hacker_typer.c, și vom dori să includă # .
Privind la pagina de manual pentru termios - Sunt ghicitul terminalul e sistemul de operare sau ceva de genul -
Nu știu cum să-l citesc.
Privind la acest lucru, se spune pentru a include aceste 2 imagini, asa ca vom face asta.
>> Primul lucru pe de o parte, dorim să ia într-o singur argument, care este fișierul ar trebui să ne deschidă.
Deci, ce vreau să fac? Cum pot verifica să văd am un singur argument?
[Elev] Dacă argc este egal. >> [Bowden] Da.
Deci, în cazul în care (argc = 2!) Printf ("Folosire:% s [fișier pentru a deschide]").
Deci, acum, dacă am rula această fără a furniza un al doilea argument - Oh, am nevoie de noua linie -
veți vedea că spune de utilizare: / hacker_typer,.
și apoi al doilea argument ar trebui să fie fișierul pe care îl doriți să îl deschideți.
Acum ce să fac?
Vreau să citesc din acest fișier. Cum am citit dintr-un fișier?
[Elev] Deschide-o tu primul. Da >>.
Deci fopen. Ceea ce nu fopen arata?
[Elev] Numele fisierului. >> [Bowden] Filename va fi argv [1].
[Elev] Și atunci ce vrei sa faci cu el, așa - >> [Bowden] Da.
Așa că, dacă nu-mi amintesc, ai putea face doar fopen om,
în cazul în care acesta va fi o cale const char * în cazul în care calea este numele fișierului,
Mod de const char *.
Dacă se întâmplă să nu ne amintim ce mod este, atunci poti sa te uiti pentru modul.
In interiorul paginilor man, caracterul slash este ceea ce se poate folosi pentru a căuta lucruri.
Așa că am scrie / mode pentru a căuta modul.
N și N sunt ceea ce se poate utiliza pentru a parcurge meciurile de căutare.
Aici se spune punctele de modul argument pentru un șir
începând cu una dintre următoarele secvențe.
Deci r, fișier text deschis pentru citire. Asta e ceea ce vrem să facem.
Pentru citirea, și vreau pentru a stoca asta.
Lucru va fi un * FILE. Acum, ceea ce vreau să fac?
Dă-mi o secundă.
Bine. Acum, ceea ce vreau să fac?
[Elev] Verificați dacă este NULL. >> [Bowden] Da.
De fiecare dată când deschideți un fișier, asigurați-vă că sunteți cu succes capabil să-l deschidă.
>> Acum vreau să fac lucrurile astea termios în cazul în care vreau să citesc mai întâi setările mele curente
și de a salva pe cei in ceva, atunci vreau să-mi modifica setările
să arunce orice caracter care tip I,
și apoi vreau să actualizați aceste setări.
Și apoi, la sfârșitul programului, vreau pentru a reveni la setările mele originale.
Deci, struct va fi de tip termios, și am de gând să doriți două dintre cele.
Primul va fi current_settings mele,
și apoi ei vor fi hacker_settings mele.
În primul rând, am de gând să doriți să salvați setările mele curente,
apoi m-am de gând să doriți să o actualizați hacker_settings,
și apoi drumul la sfârșitul programului meu, vreau să revin la setările curente.
Deci salvarea setărilor curente, modul în care funcționează, noi termios om.
Vedem că avem această tcsetattr int, int tcgetattr.
Trec într-o struct termios de indicatorul său.
Modul în care acest va arata este - I've uitat deja ce funcția a fost numit.
Copiați și lipiți-l.
Deci tcgetattr, atunci vreau să trec în struct că eu sunt salvarea informațiilor în,
care va fi current_settings,
și primul argument este descriptorul de fisier pentru lucru pe care vreau pentru a salva atributele.
Ce descriptor de fisier este este ca și cum de fiecare dată când deschideți un fișier, acesta devine un descriptor de fișier.
Când m-am fopen argv [1], acesta devine un descriptor de fișier care vă fac referire la
ori de câte ori doriți să citiți sau să-l scrie.
Asta nu e descriptor de fisier vreau să folosesc aici.
Există trei descriptori de fișier pe care trebuie în mod implicit,
care sunt standard în, din standard, si eroarea standard.
În mod implicit, cred că e iarnă în este 0, din standard este 1, iar eroarea standard este 2.
Deci ce vreau pentru a modifica setările de?
Vreau pentru a modifica setările de fiecare dată când am lovit un caracter,
Vreau să arunce acel caracter oaspeți loc de a imprima-l pe ecran.
Ce curs de apa - standard, din standard, sau eroarea standard -
răspunde la lucruri atunci când am introduceți de la tastatură? >> [Elev] Standard inch >> Da.
Deci, eu pot face fie 0 sau pot face stdin.
Primesc current_settings standard inch
>> Acum vreau să actualizați aceste setări,
asa ca prima Voi copia în ceea ce hacker_settings current_settings mele sunt.
Și modul în care munca este struct va copia doar.
Acest copiaza toate câmpurile, așa cum v-ați aștepta.
>> Acum vreau să actualizeze unele câmpuri.
Privind la termios, va trebui să citiți printr-o mulțime de acest
doar pentru a vedea ceea ce ar vrea să se uite pentru,
dar steagurile ai de gând să doriți să căutați pentru sunt ecou,
astfel de caractere de intrare ECHO Echo.
În primul rând vreau să setați - I've uitat deja ce domenii sunt.
Aceasta este ceea ce pare a fi struct.
Deci, modurile de introducere cred că vrea să se schimbe.
Ne vom uita la soluția pentru a se asigura că este ceea ce vrem să se schimbe.
Dorim să schimbe lflag, în scopul de a preveni nevoie să se uite prin toate astea.
Dorim să schimbe modurile locale.
Tu ar trebui să citiți acest lucru întreg pentru a înțelege în cazul în care totul aparține
pe care vrem să se schimbe.
Dar e în interiorul modurilor locale, în cazul în care am de gând să doriți să schimbe acest lucru.
Deci, hacker_settings.cc_lmode este ceea ce se numește.
c_lflag.
Acest lucru este în cazul în care vom intra în operatori la nivel de bit.
Suntem un fel de afara timpului, dar vom merge prin ea repede.
Acest lucru este în cazul în care vom intra în operatori la nivel de bit,
în cazul în care cred că am spus o mult timp în urmă că de fiecare dată când începeți a face cu steaguri,
ai de gând să fie folosind operatorul la nivel de bit foarte mult.
Fiecare bit din pavilion corespunde la un fel de comportament.
Deci, aici, acest indicator are o grămadă de lucruri diferite, în cazul în care toate dintre ele înseamnă ceva diferit.
Dar ceea ce vreau să fac este activa doar pe biți care corespunde ECHO.
Deci, pentru a transforma aia fac & = ¬ ECHO.
De fapt, cred că e ca și cum TECHO sau ceva de genul. Mă duc pentru a verifica din nou.
Pot să-l termios. E doar ECHO.
ECHO este de gând să fie un singur bit.
¬ ECHO este de gând să spui toate biți sunt setate la 1, ceea ce înseamnă toți indicatorii sunt setate pe true
cu excepția biți ECHO.
Prin încetarea steaguri mele locale cu acest lucru, înseamnă toate pavilion, care sunt în prezent stabilite la adevărata
va fi în continuare setată la true.
În cazul în care steagul meu ECHO este setat la true, atunci acest lucru este necesar setat la fals pe steagul ECHO.
Deci, această linie de cod doar se stinge pavilion ECHO.
Celelalte linii de cod, eu doar le copiați în interesul de timp și apoi a le explica.
În soluție, a spus el 0.
Este, probabil, mai bine să spun în mod explicit stdin.
>> Observați că fac, de asemenea, ECHO | ICANON aici.
ICANON se referă la ceva separat, ceea ce înseamnă modul canonic.
Ce înseamnă modul canonic este, de obicei, atunci când sunteți tastați în linia de comandă,
standard în nu procesa nimic până când te-a lovit newline.
Deci, atunci când nu getString, tastați o grămadă de lucruri, atunci te-a lovit newline.
Asta e atunci când este trimis la standard de inch
Asta e implicită.
Când m-am dezactiva modul canonic, acum fiecare caracter unic apăsați
este ceea ce este procesat, care este de obicei un fel de rău pentru că este lent pentru a procesa aceste lucruri,
care este motivul pentru care e bine să-l tampon în linii întregi.
Dar vreau ca fiecare personaj să fie prelucrate
deoarece nu vreau să mă aștepte la lovit newline
înainte de a le prelucrează toate personajele Am fost tastarea.
Acest lucru dezactivează modul canonic.
Chestia asta înseamnă doar atunci când prelucrează de fapt de caractere.
Acest lucru înseamnă să le proceseze imediat; cât de curând le-am tastarea, le prelucrează.
Și aceasta este funcția care este actualizarea setările mele pentru standard în,
și mijloace de TCSA face chiar acum.
Celelalte opțiuni sunt așteptați până când tot ceea ce este în prezent pe fluxul este procesată.
Asta nu contează cu adevărat.
Doar acum modifica setările mele de a fi ceea ce este în prezent în hacker_typer_settings.
Cred că am numit-o hacker_settings, asa ca hai sa schimba asta.
Schimba totul la hacker_settings.
>> Acum, la sfârșitul programului nostru vom dori să reveniți
la ceea ce este în prezent în interiorul normal_settings,
care este de gând să arate la fel ca si normal_settings.
Observați că nu s-au schimbat nici de normal_settings mele, deoarece initial l primesc.
Apoi, pentru a schimba doar le înapoi, le-am treci înapoi la sfârșitul anului.
Acest lucru a fost de actualizare. Bine.
>> Acum, în interiorul de aici voi explica doar codul din motive de timp.
Nu e acel cod de mult.
Vedem am citit un personaj din dosar. Am numit-o f.
Acum poți om fgetc, dar cum fgetc este de gând să lucreze
este doar o să se întoarcă caracterul pe care tocmai ai citit sau EOF,
care corespunde la sfârșitul fișierului sau unele se întâmplă eroare.
Suntem looping, continuă să citească un singur caracter de la dosar,
până când ne-am alerga afară de caractere pentru a citi.
Și în timp ce noi facem asta, ne așteptăm la un singur caracter de la standard inch
De fiecare dată când tastați ceva la linia de comandă,
care este citit într-un personaj de la standard de inch
Apoi, putchar este doar de gând să pună char am citit până aici din fișierul standard de ieșire.
Poți om putchar, dar e doar pune la standard afară, se imprimă acel caracter.
Ai putea, de asemenea, face doar printf ("% c", c); aceeași idee.
Asta va face cea mai mare parte a muncii noastre.
>> Ultimul lucru pe care am de gând să doriți să faceți este să fclose doar fisierul.
Dacă nu fclose, asta e o scurgere de memorie.
Dorim să fclose fișierul inițial am deschis, și cred că e ea.
Dacă vom face asta, am deja probleme.
Să vedem.
Ce a plâng?
De așteptat "int", dar argumentul este de tip "struct _IO_FILE * '.
Vom vedea dacă funcționează.
Permisă numai în C99. Augh. Bine, fac hacker_typer.
Acum avem mai multe descrieri utile.
Deci, utilizarea identificator nedeclarate "normal_settings".
Nu am sunat-l normal_settings. Am numit-o current_settings.
Deci, hai să schimbăm toate astea.
Care trece acum argument.
Voi face acest lucru 0 pentru acum.
Bine. / Hacker_typer. Cp.c.
De asemenea, nu am goli ecranul de la început.
Dar poti sa te uiti inapoi la ultimul set de probleme pentru a vedea modul în care a goli ecranul.
E doar tipărirea unor caractere
în timp ce aceasta este de a face ceea ce vreau să fac.
Bine.
Și gândesc de ce acest lucru este necesar pentru a fi 0 în loc de stdin,
care ar trebui să fie definească # 0,
acest lucru este plângându-se că -
Înainte de a când am spus că nu e descriptorii de fisiere, dar atunci va trebui, de asemenea, * de fișiere,
un descriptor de fișier este doar un singur număr întreg,
întrucât o * FILE are o grămadă de lucruri asociate cu aceasta.
Motivul pentru care trebuie să spunem 0 in loc de stdin
este faptul că stdin este o * FILE care indică lucrul care face referire descriptor de fisier 0.
Deci, chiar aici când o voi face fopen (argv [1], Primesc un * FILE spate.
Dar undeva în acel * FILE este un lucru corespunde descriptor de fisier pentru acel fișier.
Daca te uiti la pagina de manual pentru deschis, deci cred ca va trebui sa facem om deschis 3 - nope -
Man 2 deschis - Da.
Daca te uiti la pagina de deschisă, deschisă este ca o fopen de nivel inferior,
și se întoarce descriptor de fisier reală.
fopen face o grămadă de chestii pe partea de sus a deschide,
care, în loc de a se întoarce doar ca descriptor de fisier returnează un pointer întregul dosar *
în interiorul căruia este descriptor nostru mic fisier.
Deci standard se referă la lucru * FILE,
întrucât se referă la 0 doar standard de descriptor de fisier în sine.
>> Întrebări?
[Râde] suflat prin asta.
Bine. Am terminat. [Râde]
>> [CS50.TV]