[Powered by Google Translate] [Secció 5 - Més Còmode] [Rob Bowden - Harvard University] [Aquesta és CS50. - CS50.TV] Com vaig dir en el meu correu electrònic, hi ha un munt de coses que vostè pot utilitzar que no sigui l'aparell per fer realitat els butlletins de problemes. Et recomanem que ho facis en l'aparell només perquè llavors ens serà més fàcil ajudar i sabem que tot va a funcionar. Però com un exemple que es poden fer coses si, per exemple, no tenen accés a un aparell o desitja treballar al soterrani del Centre de Ciències - que en realitat tenen l'aparell massa - si vols treballar en qualsevol lloc. Un exemple s'han vist / sentit de SSH? SSH és bàsicament la mateixa que connectar-se a alguna cosa. De fet, ara mateix estic SSHed en l'aparell. Mai treballar directament en l'aparell. Aquí està l'aparell, i si mires aquí baix es veu aquesta adreça IP. Mai treball en el propi aparell; Sempre vinc a un iTerm2 finestra / finestra de terminal. Vostè pot SSH a aquesta adreça IP, ssh jharvard@192.168.129.128. Recordo aquest nombre amb molta facilitat pel fet que és un bonic patró. Però això em demanarà la contrasenya, i ara estic en l'aparell. Bàsicament, en aquest punt, si s'obre un terminal a l'interior del propi aparell, aquesta interfície, però vostè ho fa servir, és exactament el mateix com la interfície que estic fent servir aquí, però ara estem SSHed. No ha de SSH al dispositiu. Un exemple d'un altre lloc que podria SSH és que estic bastant segur que té per defecte - Oh. Més gran. Tots vostès han de tenir en compte FAS per defecte en els servidors de FAS. Per a mi, ho faria SSH a rbowden@nice.fas.harvard.edu. Va a demanar-li que la primera vegada, i vostè diu que sí. La meva contrasenya és només serà la meva contrasenya FAS. I ara, estic SSHed als servidors bons, i puc fer el que vulgui aquí. Una gran quantitat de classes que es podrien adoptar, com 124, hauran de pujar coses a aquí de presentar efectivament els seus butlletins de problemes. Però diu que no té accés al seu aparell. A continuació, pot fer coses, com aquí es dirà - Això és només les preguntes. Se li demana que ho fem en l'aparell. En lloc d'això només ho farà al servidor. Me'n vaig a descomprimir això. El problema serà que vostè està acostumat a fer servir alguna cosa com gedit o el que sigui a l'interior de l'aparell. No hauràs al servidor de FAS. Tot això és només serà aquesta interfície textual. Així que vostè podria qualsevol d'ells, tractar d'aprendre un editor de text que té. Tenen Nano. Nano és bastant fàcil d'utilitzar. Vostè pot utilitzar les seves fletxes i escriu normalment. Així que no és difícil. Si vol aconseguir realment de luxe pot usar Emacs, que jo probablement no hauria d'haver obert perquè no sé ni com tancar Emacs. X Control, Control C? Si. O pot utilitzar Vim, que és el que jo faig servir. I aquestes són les seves opcions. Si no vols fer això, vostè també pot, si ens fixem en manual.cs50.net-- Oh. En un PC, pot usar SSH PuTTY, que hauràs de descarregar per separat. En un Mac, pot simplement usar predeterminat Terminal o pot descarregar iTerm2, que és com un terminal bonic, luxós. Si vostè va a manual.cs50.net veuràs un enllaç a Notepad + +, que és el que es pot utilitzar en un PC. Et permet SFTP de Notepad + +, que és bàsicament SSH. El que això li permetrà fer és editar els teus arxius de forma local, i després cada vegada que vulguis salvar, salvarà nice.fas, on vostè pot llavors córrer. I l'equivalent en Mac serà TextWrangler. Per tant, li permet fer el mateix. Et permet editar arxius de forma local i guardar-los en nice.fas, on vostè pot llavors córrer. Així que si mai atrapat sense un aparell, té les següents opcions que encara fa els seus butlletins de problemes. L'únic problema serà que no tindrem la biblioteca CS50 perquè nice.fas per defecte no tenen això. Podeu descarregar la biblioteca CS50 - Jo no crec que sigui necessari que en aquest punt. Pot descarregar la biblioteca CS50 i copiar a nice.fas, o crec que en aquest moment no usar mai més de tota manera. O si ho fem, pot de moment, substituir-lo per les implementacions de les funcions a la biblioteca CS50 de totes maneres. Així que no hauria de ser part d'una restricció. I això és tot. Vaig a tornar a l'aplicació ara, farem tot el possible en l'aparell. Quant a les preguntes, al principi, com vaig dir en el meu correu electrònic, hem de parlar de la curta se suposava que veure. Tenim la reorientació i Tubs i aquestes tres preguntes. A què corrent no funciona com printf escriure per defecte? Així corrent. Què és un riu? Un corrent és bàsicament com si fos només una mica - Ni tan sols és una font de 1s i 0s. El corrent està demanant aquí és la sortida estàndard. I fos tan estàndard és un corrent que quan s'escriu en ell, que apareix a la pantalla. Fora Standard, per flux, això significa que vostè acaba d'escriure 1s i 0s a la mateixa, i l'altre extrem de sortida estàndard només llegeix d'aquest corrent. És només una cadena de 1s i 0s. Vostè pot escriure als rierols o pot llegir dels rierols depenent del que el corrent que realment és. Les altres dues corrents de defecte són estàndard i d'error estàndard. És estàndard en cada vegada que es GetString, està esperant per tu per matèria d'entrada. Pel que t'espera, en realitat espera a la norma en, que en realitat és el que passa quan s'escriu en el teclat. Estàs escrivint en estàndard polz Error estàndard és bàsicament equivalent a la sortida estàndard, però és especialitzada en la qual en imprimir en error estàndard, se suposa que has de imprimir només missatges d'error perquè perquè pugui diferenciar entre els missatges impresos regulars a la pantalla enfront dels missatges d'error depenent de si van ser a la sortida estàndard o error estàndard. Els arxius també. Fora estàndard, estàndard en, i l'error estàndard són corrents només especials, però en realitat qualsevol arxiu, quan s'obre un arxiu, aquest es converteix en un flux de bytes on només es pot llegir d'aquest corrent. Vostè, en la seva major part, només puc pensar en un arxiu com una seqüència de bytes. Llavors, què és el que els corrents escriure per defecte? Per Norma. Quina és la diferència entre> i >>? Algú veure el vídeo per endavant? Bé. > Serà com redirigir als arxius, i >> també va a redirigir la sortida en arxius, però aquesta vegada va a annexar a l'expedient. Per exemple, els direm és que tinc dict aquí, i les coses només a l'interior de dict és gat, gat, gos, peix, gos. Un ordre que vostè té en la línia d'ordres és gat, que només va a imprimir el que hi ha en un arxiu. Així que quan dic dict gat, que voleu imprimir gat, gat, gos, peix, gos. Això és tot gat fa. Això vol dir que s'imprimeix a la sortida estàndard gat, gat, gos, peix, gos. Si en lloc que desitgi redirigir a un arxiu, pot utilitzar> i redireccionar al que l'arxiu és. Vaig a trucar a la imatge arxiu. Així que ara si ls, vaig a veure que tinc un nou arxiu anomenat arxiu. I si l'obre, tindrà exactament el gats, condicionats a la línia d'ordres. Així que ara si ho faig de nou, llavors va a redirigir la sortida en un arxiu, i jo vaig a tenir exactament la mateixa cosa. Així que, tècnicament, es va anul · lar per complet el que teníem. I anem a veure si puc canviar dict, vaig treure gos. Ara bé, si dict gat a l'arxiu de nou, tindrem la nova versió amb el gos eliminat. Per tant, s'anul completament. En canvi, si fem servir >>, que va a adjuntar l'arxiu. Ara, en obrir l'arxiu, veiem que tenim la mateixa cosa dues vegades imprès perquè era allà un cop, llavors adjunta a l'original. Així que això és el que> i >> fer. La següent pregunta - No pregunti sobre això. L'altre que tenim és <, que si> redirecciona la sortida estàndard, fer 2>, que està redirigint error estàndard. Així que si alguna cosa sortia d'error estàndard, no es posa en txt2. Però noti si ho faig 2>, llavors és encara la impressió Hola, Rob! a la línia d'ordres perquè estic sol redirigir l'error estàndard, no estic redirigint la sortida estàndard. Error estàndard i la sortida estàndard són diferents. Si volies escriure realment l'error estàndard llavors jo podria canviar això sigui fprintf a stderr. Així printf, per defecte, s'imprimeix a la sortida estàndard. Si voleu imprimir en l'error estàndard de forma manual, llavors he de fer servir fprintf i especificar el que voleu imprimir. Si en canvi ho vaig fer stdout fprintf, llavors això és bàsicament equivalent a printf. Però fprintf l'error estàndard. Així que ara, si puc redirigir això en txt2, Hola, Rob! encara s'està imprès en la línia d'ordres ja que s'està fent visualitza com error estàndard i només estic redirigint la sortida estàndard. Si ara redirigir l'error estàndard, ja que no s'imprimeixen i txt2 serà Hola, Rob! Així que ara, vostè pot imprimir els seus errors reals en l'error estàndard i imprimir els seus missatges regulars a la sortida estàndard. I així, quan s'executa el programa, es pot executar com. / Hola aquest tipus amb el 2> perquè el programa va a funcionar amb normalitat, però qualsevol missatge d'error que vostè rep pot comprovar més endavant en el seu registre d'errors, el que els errors, i després buscar l'arxiu més tard i tindrà errors els errors que van ocórrer. Preguntes? L'últim és pipa, que es pot pensar en com prendre la sortida estàndard d'una ordre i el que és l'estàndard en la següent comanda. Un exemple d'això és ressò és quelcom línia d'ordres que només va a fer-se ressò del que em posa com a argument. No vaig a posar cometes. Echo bla, bla, bla, només voleu imprimir, bla, bla, bla. Abans, quan em va dir que havia de posar Rob en un arxiu txt perquè només puc redirigir els arxius txt, en canvi, / si em faig ressò de Rob i després canalitzar-la en. / hola, que també farà el mateix tipus de coses. Això és prendre la sortida d'aquesta comanda, l'eco Rob, i que serveixi com a input per al. / hello. Vostè pot pensar en ell com el primer ressò Rob redirigir a un arxiu i entreu en. / hola aquest arxiu que s'emeten just. Però es necessita l'arxiu temporal de la imatge. Les preguntes sobre això? La pregunta que ve serà implicar això. Què gasoducte podria utilitzar per trobar el nombre de noms únics en un arxiu anomenat nombres.txt? Les comandes que anem a voler utilitzar aquí són únics, per uniq, a continuació, wc. Vostè pot fer uniq home a mirar realment el que fa, i que només va a filtrar les línies que coincideixin adjacents de l'entrada. I l'home wc voleu imprimir la paraula nova línia, i el recompte de bytes per cada arxiu. I l'últim que anem a voler utilitzar és una espècie, que es va a ordenar només les línies d'arxiu txt. Si faig algun arxiu txt, nombres.txt, i és Rob, Tommy, Joseph, Tommy, Joseph, RJ, Rob, el que vull fer aquí és trobar el nombre de noms únics en aquest arxiu. Llavors, què hauria de ser la resposta? >> [Estudiant] 4. Sí >>. Hauria de ser 4, ja que Rob, Tommy, Joseph, RJ són els únics noms únics en aquest arxiu. El primer pas, si acabo de fer recompte de paraules en nombres.txt, això és en realitat em diu tot. Això és en realitat la impressió - A veure, home wc - noves línies, paraules, i el recompte de bytes. Si només es preocupen per les línies, llavors només pot fer wc-l nombres.txt. Així que aquest és el pas 1. Però jo no vull wc-l nombres.txt nombres.txt perquè només conté tots els noms, i vull filtrar els éssers no únics. Així que si ho faig nombres.txt uniq, que no acaba de donar-me el que vull perquè els noms duplicats encara hi són. Per què és això? Per què no uniq fer el que vull? [Estudiant] Els duplicats no són [inaudible] >> Si. Recordeu que la pàgina de manual per uniq diu filtre de línies coincidents adjacents. No són adjacents, per la qual cosa no els filtra. Si ordenar en primer lloc, nombres.txt tipus posarà totes les línies duplicades entre si. Així que ara nombres.txt espècie és això. Vaig a voler utilitzar això com l'entrada a uniq, que és | uniq. Això em dóna Joseph, RJ, Rob, Tommy, i vull utilitzar això com l'entrada a wc-l, que em donarà 4. Com diu aquí, el gasoducte podria utilitzar? Vostè pot fer un munt de coses com l'ús d'una sèrie d'ordres on s'utilitza la sortida d'una ordre com a entrada per l'ordre següent. Vostè pot fer un munt de coses, un munt de coses intel · ligents. Preguntes? Bé. Això és per les canonades i la redirecció. Ara anem a les coses reals, les coses codificació. Dins d'aquest PDF, podràs veure aquesta comanda, i vol executar aquesta comanda a l'aparell. wget és la comanda per només aconseguir alguna cosa d'Internet, en el fons, així wget URL i això. Si va anar a la següent adreça URL al navegador, es descarrega l'arxiu. M'acaba de fer clic, de manera que l'arxiu descarregat per a mi. Però escriure wget d'aquesta cosa dins de la terminal només va a descarregar en el seu terminal. Tinc section5.zip, i vol descomprimir section5.zip, que et donarà una carpeta anomenada Secció 5, que tindrà tots els arxius que utilitzarem avui dia dins d'ella. Com els noms d'aquests programes suggereixen arxius, són una mica buggy, pel que la seva missió és esbrinar per què utilitzar gdb. Tothom els ha descarregat / saber com els ha descarregat en el seu electrodomèstic? Bé. Execució de ./buggy1, dirà segmentation fault (core dumped), que cada vegada que rep un error de segmentació, és negatiu. En quines circumstàncies es pot aconseguir una violació de segment? [Estudiant] desreferenciación un punter nul. Sí >>. Així que és un exemple. Desreferenciación un punter nul vas a aconseguir una violació de segment. El que significa és una violació de segment que està tocant de memòria que no s'ha de tocar. Així desreferencia un punter nul està tocant l'adreça 0, i, bàsicament, tots els ordinadors avui en dia diuen que el 0 és l'adreça de memòria que no s'ha de tocar. Així que per això es produeix una eliminació de referències a punters nuls en una violació de segment. Quan et quedes a no inicialitzar un punter, llavors té un valor escombraries, i així, quan s'intenta eliminar la referència que, amb tota probabilitat, vostè està tocant memòria que està al mig del no-res. Si li passa a tenir sort i el valor de les escombraries pas perquè apunti a algun lloc a la pila o alguna cosa, després quan desreferencia punter que els que no s'ha inicialitzat, res sortirà malament. Però si s'està assenyalant, per exemple, en algun lloc entre la pila i el heap, o és simplement apuntant a algun lloc que no ha estat utilitzat pel programa, però, llavors vostè està tocant de memòria que no s'ha de tocar i segfault tu. En escriure una funció recursiva i recursivament massa vegades i la seva pila és massa gran i xoca amb les coses de la pila que no ha de xocar amb, estàs tocant de memòria que no s'ha de tocar, per la qual cosa violació de segment. Això és el que és una violació de segment. També és la mateixa raó que si vostè té una cadena com - anem a tornar al programa anterior. En hello.c--sóc només farà altra cosa. char * s = "hola món"; Si utilitzo * s = alguna cosa o es [0] = 'X'; així que hola. / hola, per què aquesta violació de segment? Per què aquesta violació de segment? Què espera que passi? Si ho fes printf ("% s \ n", s), el que es pot esperar per ser imprès? [Estudiant] X hello. Sí >>. El problema és que quan es declara una cadena com aquesta, s és un punter que anirà a la pila, i el que s està assenyalant és aquesta cadena que està contingut en memòria de només lectura. Així que només pel nom, la memòria de només lectura, vostè ha d'aconseguir la idea que si intenta canviar el que hi ha en memòria de només lectura, vostè està fent una cosa que no hauria d'estar fent amb la memòria i la violació de segment tu. Això és realment una gran diferència entre char * s i char s []. Així char s [], ara aquesta cadena es posarà a la pila, i la pila no és de només lectura, el que significa que això hauria de funcionar perfectament bé. I ho fa. Recordeu que quan faig char * s = "hola món", s és en si mateix a la pila però assenyala sa en un altre lloc, i que en un altre lloc passa a ser de només lectura. Però char s [] és només una cosa a la pila. Així que aquest és un altre exemple d'una violació de segment succeint. Vam veure que ./buggy1 va donar lloc a una violació de segment. En teoria, no hauria de mirar buggy1.c immediatament. En el seu lloc, anem a mirar a través de gdb. Tingueu en compte que quan vostè aconsegueix segmentation fault (core dumped), rep aquest arxiu a través d'aquí es diu nucli. Si ls-l, veurem que el nucli és normalment un arxiu bastant gran. Aquest és el nombre de bytes de l'arxiu, de manera que sembla que és una cosa de 250 kilobytes. La raó d'això és que el que el bolcat del nucli és la manera Es presenta quan el programa es bloqueja, l'estat de la memòria del seu programa només es copia i s'enganxa en aquest arxiu. Això s'aboquen en aquest arxiu. Aquest programa, alhora que corria, va passar a tenir un ús de memòria de prop de 250 kilobytes i això és el que he abocament en aquest arxiu. Ara es pot veure a l'arxiu si ho fem gdb nucli buggy1. Només podem fer gdb buggy1, i que només es posarà en marxa gdb amb regularitat, utilitzant buggy1 com el seu arxiu d'entrada. Però si ho fa gdb nucli buggy1, llavors està específicament posarà en marxa gdb en veure que l'arxiu principal. I dient buggy1 gdb 1/2 sap que aquest arxiu central prové del programa buggy1. Així gdb buggy1 nucli va a portar immediatament als quals el programa va passar a acabar. Veiem aquí el programa va acabar amb senyal 11, fallada de segmentació. Ens va passar a veure una línia de muntatge, el que probablement no és molt útil. Però si escriu bt o backtrace, això serà la funció que ens dóna la llista dels nostres marcs de pila actual. Així backtrace. Sembla que només tenim dos marcs de pila. El primer és el nostre marc de pila principal, i el segon és el marc de pila per a aquesta funció que ens ha tocat estar, que sembla que només tenim el codi assemblador per. Així que anem a tornar a la nostra funció principal, i per això podem fer un marc, i crec que també podem fer cap avall, però gairebé mai ho fan cap avall - amunt o avall. Si. A dalt ia baix. Fins que ens porta a un marc de pila, sota et porta a baix un marc de pila. Tendeixo a cap utilitat específica. Acabo de dir específicament el quadre 1, que es vagi al fotograma amb l'etiqueta 1. Quadre 1 ens posarà en marc de pila principal, i diu que aquí la línia de codi que ens trobem en. Si volíem un parell de línies de codi, es pot afirmar llista, i que ens donarà totes les línies de codi que l'envolten. La línia que estava en segfaulted 6: if (strcmp ("CS50 roques", argv [1]) == 0). Si no està clar, però, vostè pot anar directament des d'aquí només pensar per què segfaulted. Però podem fer un pas més i dir: "Per què argv [1] violació de segment?" Anem a imprimir argv [1], i sembla que és 0x0, que és el punter nul. Estem strcmping CS50 roques i nuls, per el que va a violació de segment. I per què és argv [1] null? [Estudiant] A causa que no li va donar cap argument de línia d'ordres. Si. Nosaltres no li va donar cap argument de línia d'ordres. Així ./buggy1 només tindrà argv [0] sigui ./buggy1. No tindrà un argv [1], de manera que va a violació de segment. Però si, en canvi, ho faig només CS50, dirà que et donen un D perquè això és el que se suposa que ha de fer. Quant a buggy1.c, se suposa que d'imprimir "S'obté una D" - Si argv [1] no és "CS50 roques", "Vostè obté una D", del "S'obté una A!" Així que si volem una A, necessitem això per comparar com a veritable, el que significa que es compara amb 0. Així que argv [1] ha de ser "pedres" CS50. Si vols fer això a la línia de comandes, ha d'utilitzar \ per escapar a l'espai. Així CS50 \ roques i s'obté una A! Si no fa la barra invertida, per què no funciona? [Estudiant] Es tracta de dos arguments diferents. Sí >>. Argv [1] serà CS50, i argv [2] serà roques. Bé. Ara ./buggy2 va a violació de segment nou. En lloc d'obrir l'arxiu amb el seu nucli, només haurem d'obrir buggy2 directament, així gdb buggy2. Ara bé, si acaba d'executar el programa, llavors dirà el programa va rebre SIGSEGV, que és l'error de segmentació del senyal, i aquí és on va succeir a succeir. Quant al nostre backtrace, veiem que estàvem al oh_no funció, que va ser cridat pel Dinky funció, que va ser cridat pel binky funció, que va ser convocada per principal. També podem veure els arguments per a aquestes funcions. L'argument de mala mort i binky va ser d'1. Si tenim a punt la funció oh_no, veiem que només està fent oh_no char ** s = NULL; * S = "BOOM"; Per què no? [Estudiant] No es pot eliminar la referència al punter nul? Sí >>. Això és només dir s és NULL, independentment de si que passa a ser un char **, que, depenent de com s'interpreti, podria ser un punter a un punter a una cadena o una matriu de cadenes. És s és NULL, pel que * s es desreferencia un punter nul, de manera que aquest es va a estavellar. Aquesta és una de les maneres més ràpides li sigui possible violació de segment. És simplement declarar un punter nul i segfaulting immediatament. Això és el que oh_no està fent. Si pugem un marc, llavors entrarem en la funció que va cridar oh_no. He de fer això. Si no s'introdueix una ordre i premeu Enter, s'acaba de repetir la comanda anterior que es va executar. Som al quadre 1. Afegir aquest marc, veiem aquí és la nostra funció. Vostè pot colpejar llista de nou, o pots fer la llista 20 i una llista de més. La funció Dinky diu que si i és 1, llavors aneu a la funció oh_no, sinó va a la funció furtiu. I sé que és 1 perquè ens ha tocat veure aquí Dinky que s'ha anomenat amb l'argument 1. O pot simplement puc imprimir i dirà i és 1. Actualment estem en mala mort, i si ens anem a un altre marc, sabem que acabarà en binky. Amunt. Ara estem en binky. Afegir aquesta funció - la llista d'abans del descans em va interrompre - el que va començar com si i és 0, llavors anem a cridar oh_no, que truqui mala mort. Sabem que era 1, de manera que va cridar de mala mort. I ara que estem de tornada en main, i principal és només serà int i = rand ()% 3; Això només va a donar-li un nombre aleatori que és 0, 1 o 2. Es dirà binky amb aquest nombre, i el tornarà 0. Quant a això, caminant a través del programa de forma manual sense executar immediatament, hauria d'establir un punt de trencament en la principal, el que significa que quan es corre el programa seu programa s'executa fins que s'arriba a un punt d'inflexió. Llavors, executar el programa, s'executarà i després arribarà a la funció principal i deixar de córrer. Ara estem dins de la principal, i el pas següent o ens portarà a la següent línia de codi. Vostè pot fer el pas o la següent. Colpejar següent, ara m'ha estat ajustat a rand ()% 3, de manera que podem imprimir el valor de i, i ho vaig a dir que és 1. Ara bé, no importa si fem servir o següent pas. Suposo que importava en l'anterior, però ens agradaria utilitzar a continuació. Si fem servir pas, entrem en la funció, el que significa veure la cosa real que està succeint dins de Binky. Si fem servir següent, llavors vol dir anar sobre la funció i només ha d'anar a la següent línia de codi en la nostra funció principal. Aquí mateix, en aquesta línia, jo estava on deia rand ()% 3; si ho vaig fer pas, entraria en l'execució de rand i veure el que està passant allà, i podria passar per la funció rand. Però no es preocupen per la funció rand. Només vull anar a la següent línia de codi al principal, de manera que utilitzarà a continuació. Però ara sí que m'importa sobre la funció binky, així que vull entrar en això. Ara estic en binky. La primera línia de codi que dirà if (i == 0), faig un pas, veiem que acabem en mala mort. Si nosaltres, les coses de la llista, veiem que el revisi i és = 0. i no és igual a 0, de manera que es va anar a la condició més, que es dirà Dinky (i). És possible que es confongui. Si només s'observa en aquestes línies directament, es podria pensar if (i == 0), bé, llavors vaig donar un pas i ara estic en Dinky (i), es podria pensar que ha de significar i = 0 o alguna cosa així. No Només vol dir que sap que pot enganxar directament a la línia Dinky (i). Perquè no és 0, el següent pas no s'acabarà en l'altre. Més no és una línia que va a parar al. És només anirà a la següent línia en realitat pot executar, que és Dinky (i). En entrar a Dinky (i), veiem if (i == 1). El que sí que sé = 1, així que quan fem un pas, sabem que acabarem en oh_no i = 1, perquè crida a la funció oh_no, que es pot caminar en, que s'establirà char ** s = NULL i immediatament "BOOM". I després mirant realment l'aplicació de buggy2, això, m'acaba d'obtenir un nombre aleatori - 0, 1, o 2 - anomenat Binky, que si i és 0 en diu oh_no, del que anomena Dinky, que arriba fins aquí. Si i és 1, anomenada oh_no, que cridi Slinky, que ve aquí, si i és 2, trucar oh_no. Ni tan sols crec que hi hagi una manera - Algú veu una manera de fer d'aquest un programa que no violació de segment? Perquè si no em falta alguna cosa, si i és 0, vostè immediatament violació de segment, res més que anar a una funció que si i és vostè un error de segmentació, res més que anar a una funció en la qual si i és 2 que violació de segment. Així que no importa el que facis, violació de segment. Crec que una manera d'arreglar seria en lloc de fer char ** s = NULL, vostè podria malloc espai per aquesta cadena. Podríem fer malloc (sizeof) - sizeof què? [Estudiant] (char) * 5? >> Això sembla correcte? Assumeixo que això va a funcionar si realment va funcionar, però no és el que estic buscant. Mira el tipus de s. Anem a afegir * int, int mode * x. M'agradaria fer malloc (sizeof (int)). O hom volia una matriu de 5, ho faria (sizeof (int) * 5); Què passa si tinc un int **? El que jo faria malloc? [Estudiant] Mida del punter. Sí >>. (Sizeof (int *)); El mateix aquí. Vull (sizeof (char *)); Això va a assignar espai per al punter que apunta a "BOOM". No cal fer reserva espai per "BOOM" en si perquè això és bàsicament equivalent al que vaig dir abans de char * x = "BOOM". "BOOM" ja existeix. Succeeix que hi ha a la regió de només lectura de la memòria. Però el que ja existeix, el que significa que aquesta línia de codi, si s és un char **, llavors s * és un char * i que està configurant aquest char * per indicar "BOOM". Si volia copiar "BOOM" en si, llavors jo hauria de assignar espai per al s. Faré * s = malloc (sizeof (char) * 5); Per què cinc? 4 Per què no? Sembla que "BOOM" és de 4 caràcters. >> [Estudiant] El caràcter nul. Si. Totes les cadenes van a necessitar el caràcter nul. Ara puc fer alguna cosa com strcat - Quina és la funció per copiar una cadena? [Estudiant] cpy? >> Strcpy. home strcpy. Així strcpy o strncpy. strncpy és una mica més segur ja que es pot especificar amb exactitud el nombre de caràcters, però en aquest cas no importa perquè el que sabem. Així strcpy i mirar en els arguments. El primer argument és el nostre destí. El segon argument és la nostra font. Anem a copiar al nostre destí * s el punter "BOOM". Per què voldria fer això amb un strcpy en lloc del que teníem abans de * s = "BOOM"? Hi ha una raó potser voldreu fer això, però què és això? [Estudiant] Per canviar alguna cosa al "BOOM". Sí >>. Ara puc fer alguna cosa com s [0] = 'X'; perquè els punts es al munt i espai que en el munt que s està apuntant a és un punter a més espai a la pila, que és l'emmagatzematge de "BOOM". Així que aquesta còpia de "BOOM" s'emmagatzema en el munt. Hi tècnicament dues còpies del "boom" del nostre programa. No és el primer que s'acaba de donar per aquesta constant cadena "BOOM", i la segona còpia del "boom", strcpy va crear la còpia de "BOOM". No obstant això, la còpia de "BOOM" s'emmagatzema a la pila, i el munt ets lliure de canviar. La pila no és de només lectura, el que significa que es [0] permetrà que vostè canviï el valor de "BOOM". Això permetrà canviar aquests caràcters. Preguntes? Bé. Passant a buggy3, gdb anem buggy3. Acabem de córrer i veiem que obtenim una violació de segment. Si backtrace, només hi ha dues funcions. Si pugem a la nostra funció principal, veiem que segfaulted en aquesta línia. Així que només mirar aquesta línia, for (int fila = 0; fgets això fa NULL no és igual; línia + +). El nostre quadre anterior es deia _IO_fgets. Vostè veurà que una gran quantitat de funcions integrades C, que quan arribi la violació de segment, hi haurà noms de les funcions realment críptic com aquest _IO_fgets. Però això va a relacionar amb aquesta convocatòria fgets. En algun lloc dins d'aquí, estem segfaulting. Si ens fixem en els arguments a fgets, podem imprimir buffer. Anem a imprimir com - Oh, no. Imprimir no funcionarà exactament com jo vull que faci. Anem a veure el programa actual. Buffer és una matriu de caràcters. Es tracta d'una matriu de caràcters de 128 caràcters. Així que quan dic memòria intermèdia d'impressió, d'imprimir aquests 128 caràcters, que suposo que és el que s'espera. El que es busca és imprimir la direcció de memòria intermèdia, però que en realitat no em diu molt. Així que quan se m'acut dir aquí amortidor x, que em mostra 0xbffff090, que, si et recordes d'abans o cap punt, Oxbffff tendeix a ser una regió de pila-ish. La pila tendeix a començar en algun lloc una mica menys de 0xc000. Només per veure aquesta direcció, ja sé que el buffer està passant a la pila. Reinici del meu programa, executar, dalt, esmorteir el que vam veure va ser aquesta seqüència de caràcters que són més o menys sentit. Després d'imprimir el fitxer, un arxiu sembla? [Estudiant] Null. Sí >>. L'arxiu és un de tipus * FILE, pel que és un punter, i el valor d'aquest punter és nul. Així fgets es tractarà de llegir aquest punter en forma indirecta, però per tal d'accedir a aquest punter, que ha de deixar de fer referència. O bé, per tal d'accedir al que ha d'apuntar a, It desreferencia. Així que és eliminar la referència a un punter nul i segfaults ella. Podria haver reiniciat allà. Si trenquem al nostre punt principal i córrer, la primera línia de codi és char * filename = "nonexistent.txt"; Això hauria de donar una pista bastant gran de per què aquest programa falla. Escrivint següent em porta a la següent línia, en què obrir aquest arxiu, i llavors immediatament entrar en la nostra línia, on una vegada em va colpejar proper, serà violació de segment. Algú vol fer una raó per la qual podria ser segfaulting? [Estudiant] El fitxer no existeix. Sí >>. Això se suposa que és un indici que cada vegada que va a obrir un arxiu que vostè necessita per comprovar que l'arxiu existeix en realitat. Així que aquí, "nonexistent.txt"; En nom de fitxer fopen per a la lectura, llavors necessito dir if (arxiu == NULL) i dir printf ("El fitxer no existeix!" o - millor encara - filename); return 1; Així que ara comprovem si és NULL abans de realment continuar i intentar llegir aquest arxiu. Podem refer només per veure que funciona. Tenia la intenció d'incloure una nova línia. Així que ara nonexistent.txt no existeix. Sempre s'ha de comprovar si aquest tipus de coses. Sempre s'ha de comprovar per veure si fopen retorna NULL. Sempre s'ha de comprovar per assegurar-se que malloc no torna NULL, o en cas contrari violació de segment. Ara buggy4.c. En execució. Suposo que això està a l'espera de l'entrada o possiblement bucle infinit. Sí, és un bucle infinit. Així buggy4. Sembla que estem en bucle infinit. Podem dividir principal, executeu el programa. En gdb, sempre que l'abreviatura que s'utilitza és ambigua o abreviatures especials que ofereixen per a vostè, llavors vostè pot utilitzar n per utilitzar a continuació en lloc d'haver de escriure al costat tot el camí. I ara que m'he colpejat núm una vegada, només pot prémer Enter per continuar al costat en lloc d'haver de prémer Enter n, n Intro, núm Intro. Sembla com si estigués en una mena de bucle perquè s'ajusti array [i] a 0. Sembla que mai estic sortint d'aquest bucle for. Si jo puc imprimir, així que és 2, llavors vaig a anar després. Vaig a imprimir i, i és 3, llavors vaig a anar després. Vaig a imprimir i i i és 3. A continuació, imprimiu i, i és 4. En realitat, la impressió sizeof (array), el que la mida de la matriu és de 20. Però sembla que hi ha alguna ordre gdb especial per anar fins que alguna cosa passa. És com fixar una condició sobre el valor de la variable. Però jo no me'n recordo del que és. Així que si segueix endavant - Què estava dient? Què has portat cap amunt? [Estudiant] No mostrar afegeixo - >> Yeah. Així que mostrar el que puc ajudar. Si ens limitem a mostrar i, es va posar aquí quin és el valor de i és així que no has de imprimir cada vegada. Si seguim endavant següent, veiem 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5. Alguna cosa va malament, molt malament, i jo es posa a 0. Quant a buggy4.c, veiem tot el que passa és array int [5]; for (i = 0; i <= sizeof (array); i + +) array [i] = 0; Què és el que veiem que està malament aquí? Com una pista, quan estava fent el gdb buggy4 - anem a trencar la marxa principal, - Jo tenia la impressió sizeof (array) només per veure el que la condició és on finalment esclatés. On sóc? He córrer? Jo no declarar encara. Així imprimir sizeof (array) i això és 20, que s'espera ja que el meu matriu és de mida 5 i és de 5 punts, de manera que la cosa sencera ha de ser de 5 * sizeof (int) bytes, on sizeof (int) tendeix a ser 4. Així sizeof (array) és 20. Què hauria de ser? [Estudiant] Dividits per sizeof (int). >> Sí, / sizeof (int). Sembla que encara hi ha un problema aquí. Crec que això només hauria de ser < ja que és gairebé sempre > [Bowden] Sí Quan anem més enllà del final de la nostra matriu, d'alguna manera que l'espai que estem anul · lant està ignorant el valor de i. I si ens fixem en buggy4, trencar marxa principal, anem a imprimir la direcció de i. Sembla com si fos bffff124. Ara anem a imprimir la direcció de la matriu [0]. 110. Què passa amb [1]? 114. [2], 118. 11c, 120. array [5] és bfff124. Així array [5] té la mateixa direcció que jo, el que significa que la matriu [5] és i. Si tenen la mateixa direcció, són la mateixa cosa. Així que quan ens vam posar array [5] a 0, estem i en 0. I si vostè pensa sobre això en termes de la pila, int i es declara en primer lloc, el que significa que té una mica d'espai a la pila. Llavors matriu [5] s'assigna, de manera que després de 20 bytes s'assignen a la pila. Així que s'assignaran en primer lloc, a continuació, aquests 20 bytes s'assignen. Així que passa just abans de la matriu, i pel camí, com vaig dir la setmana passada, quan sigui tècnicament la pila creix cap avall, Quan s'indexa en una matriu, se'ns garanteix que la posició de 0 a de la matriu sempre passa abans de la primera posició en la matriu. Això és una mica com ho va fer la setmana passada. Observeu que a la part inferior tenim l'adreça 0 i en la part superior tenim Max direcció. La pila sempre creix cap avall. Diguem que em assignen. Assignem sencer i, el que significa que direm aquí sencer i s'assignen. Llavors assignem la nostra matriu de 5 punts, el que significa que sota això, des que la pila creix cap avall, aquests 5 nombres enters s'assignen. No obstant això, a causa de com funcionen les matrius, es té la garantia que la primera posició en la matriu sempre té una direcció inferior a la segona cosa a la matriu. Així posició 0 array sempre ha de passar primer a la memòria, mentre que la posició de la matriu 1 ha de succeir després que i posició de matriu 2 ha de succeir després d'això, el que significa que la posició 0 array que succeiria en algun lloc per aquí, posició de matriu 1 passaria per sobre d'aquest perquè mou cap amunt significa majors adreces des de la direcció màxima és d'aquí. Així array [0] fins aquí, array [1] aquí, array [2] fins aquí, array [3] fins aquí. Note com abans hem destinat sencer i tot el camí fins aquí, a mesura que avancem més i més en el nostre arsenal, estem cada vegada més a prop i més a prop del nostre sencer i. El que passa és que array [5], que és una posició més enllà de la nostra matriu, és exactament on sencer i va passar a ser assignat. Així que aquest és el punt en què ens ha tocat estar colpejant l'espai a la pila que es van assignar sencer i, i estem establint que a 0. Això és el que funciona. Preguntes? Si. [Estudiant] És igual. Bé. [Estudiant] Com evitar aquest tipus d'errors? Aquesta classe d'errors? No utilitzeu C com a llenguatge de programació. Utilitza un llenguatge que té límits de la matriu de comprovació. Sempre que vostè té cura, vostè només ha d'evitar anar més enllà dels límits de la matriu. [Estudiant] Així que aquí quan vam ser més enllà dels límits de la matriu - [Bowden] Aquí és on les coses comencen a anar malament. >> [Estudiant] Oh, està bé. Sempre que es mantingui dins de la memòria assignada per la matriu, estàs bé. No obstant això, C no realitza comprovacions d'error. Si ho faig matriu [1000], que amb molt de gust n'hi ha prou amb modificar passi el que passi - Es va al principi de la matriu, llavors es va després de 1000 posicions i l'estableix a 0. No fa cap comprovació que oh, això no té realment 1000 coses en ell. 1000 és molt més del que hauria d'estar canviant, mentre que Java o alguna cosa que vostè obtindrà la matriu fora de límits índex o l'índex d'excepció límits. És per això que una gran quantitat de llenguatges d'alt nivell té aquestes coses on si vostè va més enllà dels límits de la matriu, que no vagi de manera que vostè no pot canviar les coses des de baix que i llavors les coses van molt pitjor del que acaba d'aconseguir una excepció dient que anava més enllà del final de la matriu. [Estudiant] I així ho hem canviat el <= a només > [Bowden] Yeah. Ha de ser > [Estudiant] Dret. Més preguntes? Bé. [Estudiant] Tinc una pregunta. Sí >>. [Estudiant] Quina és la variable de matriu real? [Bowden] Igual que el que és matriu? Matriu en si és un símbol. És només l'adreça de començament dels 20 bytes que es fa referència. Vostè pot pensar en ell com un punter, però és un punter constant. Així que les coses es compila, la matriu de la variable ja no existeix. [Estudiant] Llavors, com trobar la mida de la matriu? Mida de matriu es refereix a la mida d'aquest bloc que fa referència a aquest símbol. Quan faig alguna cosa com printf ("% p \ n", array); anem a córrer. Què acabo de fer el mal? 'Array' Matriu declarat aquí. Oh, aquí dalt. Clang és intel · ligent, i passa a notar que jo vaig declarar la matriu com 5 elements però estic en posició d'indexació 1000. Es pot fer això perquè aquests són només constants. Només es pot arribar a adonar-se que jo vaig més enllà dels límits de la matriu. Però noti abans, quan havíem de ser incorrecta, no és possible de determinar quants valors que podria assumir, pel que no pot determinar que i anava més enllà del final de la matriu. Això és només Clang ser intel · ligent. Però ara fer buggy4. Llavors, què estic fent malament? Implícitament es declara funció de biblioteca 'printf'. Vaig a voler incloure # . Bé. Ara executa buggy4. Impressió del valor de la matriu com ho vaig fer aquí, imprimir com un punter imprimeix una cosa que són aquestes - bfb8805c - que és una mica de direcció que és a la regió de pila-ish. Matriu en si és com un punter, però no és un punter real, des d'un punter normal que podem canviar. Array és només una constant. Els 20 blocs de memòria començarà a les 0xbfb8805c direcció. Així bfb8805c través d'aquesta adreça +20--o suposo -20 - és tota la memòria assignada a aquesta matriu. Array, la pròpia variable no s'emmagatzema en qualsevol lloc. Quan està compilant el compilador - onada mà-hi - el compilador només s'utilitzen on coneix matriu a ser. Se sap que la matriu s'inicia, i pel que sempre pot fer les coses en termes de compensacions d'aquest principi. No es necessita una variable en si per representar matriu. Però quan faig alguna cosa com int * p = array, ara que p és un punter que apunta a la matriu, i ara p en realitat no existeix a la pila. Sóc lliure per canviar p. Que puc fer p = malloc. Per tant, inicialment va assenyalar a la matriu, i ara apunta a un cert espai en el munt. No puc fer array = malloc. Si Clang és intel · ligent, em cridis la dreta del pal. De fet, estic bastant segur gcc de fer això també. Així que tipus de matriu 'int [5]' no és assignable. No es pot assignar alguna cosa a un tipus de matriu perquè la matriu és simplement una constant. És un símbol que fa referència a aquests 20 bytes. No ho puc canviar. [Estudiant] I on és la mida de la matriu emmagatzemada? [Bowden] No s'emmagatzema en qualsevol lloc. És quan està compilant. Llavors, on és la mida de la matriu emmagatzemada? Només es pot utilitzar sizeof (array) dins de la funció que la matriu s'hagi declarat. Així que si faig alguna funció, foo, i ho faig (int array []) printf ("% d \ n", sizeof (array)); i després per aquí em diuen foo (array); dins d'aquesta funció - anem a executar-lo. Es tracta de ser intel · ligent Clang nou. M'està dient que sizeof paràmetre de matriu en funció tornarà mida de 'int'. Això seria un error si no és el que jo volia que succeís. Anem realment apagar Werror. Advertència. Les advertències estan bé. Encara es compilarà el temps que té una advertència. . / A.out voleu imprimir 4. L'advertència que s'ha generat és un indicador clar del que va sortir malament. Aquesta matriu int és només voleu imprimir sizeof (int *). Fins i tot si poso matriu [5] aquí, segueix sent només voleu imprimir sizeof (int *). Així que tan aviat com vostè ho passa a una funció, la distinció entre matrius i punters és inexistent. Això passa a ser una matriu que es va declarar a la pila, però tan bon punt es passa aquest valor, que 0xbf bla, bla, bla, en aquesta funció, a continuació, aquest punter apunta a la matriu en la pila. Així que això significa que sizeof només s'aplica en la funció que el conjunt va ser declarat, el que significa que quan s'està compilant aquesta funció, Clang quan passa a través d'aquesta funció, veu matriu és una matriu de int mida 5. Així ho veu sizeof (array). Bé, això és 20. Això és en realitat com sizeof bàsicament funciona per a gairebé tots els casos. Sizeof no és una funció, és un operador. No cridi a la funció sizeof. Sizeof (int), el compilador només la traduirà a 4. Ho tens? Bé. [Estudiant] Quina és la diferència entre sizeof (array) de principal i en foo? Això és perquè estem dient sizeof (array), que és de tipus int *, mentre que la matriu d'aquí baix no és de tipus int *, és una matriu int. [Estudiant] Per tant, si vostè tenia el paràmetre de matriu [] en lloc de la matriu * int, Vol dir que encara podria canviar matriu perquè ara és un punter? [Bowden] D'aquesta manera? >> [Estudiant] Yeah. Es pot canviar array dins de la funció ara? [Bowden] Vostè podria canviar la matriu en ambdós casos. En ambdós casos, vostè és lliure de dir array [4] = 0. [Estudiant] Però pot vostè fer punt matriu a una altra cosa? [Bowden] Oh. Si. En qualsevol dels casos - >> [estudiant] Yeah. [Bowden] La distinció entre array [] i una matriu int *, no n'hi ha cap. També pot obtenir alguns matriu multidimensional aquí per alguna sintaxi convenient, però encara és només un indicador. Això vol dir que sóc lliure de fer array = malloc (sizeof (int)), i ara apunten a un altre lloc. Però igual que com això funciona sempre i sempre, canviar aquesta matriu fent que apunti a una altra cosa no canvia aquesta matriu per aquí perquè és una còpia de l'argument, no és un punter a aquest argument. I de fet, així com la indicació més que és exactament el mateix - ja hem vist el que s'imprimeix la matriu d'impressió - el que si imprimim la direcció de la matriu o la direcció de la direcció de la matriu a qualsevol d'aquests? Anem a passar per alt això. Bé. Això està bé. Ara està en marxa. / A.out. Matriu d'impressió, i després imprimir la direcció de la matriu, són la mateixa cosa. Matriu simplement no existeix. Se sap quan voleu imprimir array, que voleu el símbol que es refereix a aquests 20 bytes. Impressió de la direcció de la matriu, així, la matriu no existeix. No té una direcció, de manera que només imprimeix la direcció d'aquests 20 bytes. Així que es compila cap avall, com si buggy4 compilat. / A.out, matriu és inexistent. Punters existeix. Les matrius no. Els blocs de memòria que representa el conjunt encara existeix, però la matriu de variables i variables d'aquest tipus no existeixen. Aquests són com les principals diferències entre les matrius i punters estan tan aviat com feu trucades a funcions, no hi ha cap diferència. Però dins de la funció que la pròpia matriu es declara, sizeof funciona de manera diferent ja que voleu imprimir la mida dels blocs en lloc de la mida del tipus, i no es pot canviar perquè és un símbol. Impressió de la cosa i la direcció de la cosa imprimeix la mateixa cosa. I això és més o menys la mateixa. [Estudiant] Podria vostè dir que una vegada més? Em podria haver passat alguna cosa per alt. Matriu d'impressió i la direcció de la matriu imprimeix la mateixa cosa, mentre que si s'imprimeix un punter davant de la direcció del punter, l'única cosa que imprimeix la direcció del que estàs assenyalant, l'altre imprimeix la direcció del punter a la pila. Pot canviar un punter, no es pot canviar un símbol de matriu. I punter sizeof s'imprimirà la mida d'aquest tipus de punter. Així int * p sizeof (p) d'imprimir 4, però int array [5] impressió sizeof (array) es va a imprimir 20. [Estudiant] Així int array [5] s'imprimiran 20? Sí >>. És per això que dins buggy4 quan el que solia ser sizeof (array) això feia jo <20, que no és el que volíem. Volem i <5. >> [Estudiant] Bé. [Bowden] I després, tan aviat com vostè comenci a passar a les funcions, si ho féssim int * p = array; dins d'aquesta funció, que bàsicament pot utilitzar p i matriu en exactament les mateixes formes, excepte pel problema sizeof i el problema canviant. Però p [0] = 1, és el mateix que dir array [0] = 1; I tan bon punt diem foo (array), o foo (p); dins de la funció foo, aquesta és la mateixa trucada dues vegades. No hi ha cap diferència entre aquestes dues trucades. Tothom bo en això? Bé. Tenim 10 minuts. Tractarem d'aconseguir a través d'aquest programa Typer Hacker, Aquest lloc web, que va sortir l'any passat o alguna cosa així. És només suposa que és com s'escriu l'atzar i s'imprimeix - Qualsevol que sigui l'arxiu que resulta haver carregat és el que sembla que està escrivint. Sembla una espècie de codi del sistema operatiu. Això és el que volem fer. Vostè ha de tenir un binari executable anomenat hacker_typer que porta en un sol argument, l'arxiu de "tipus hacker." Executar l'executable ha d'esborrar la pantalla i després imprimir un caràcter del fitxer passat com cada vegada que l'usuari prem una tecla. Així que qualsevol tecla que premeu, s'ha de rebutjar i en el seu lloc mostra un personatge de l'arxiu que és l'argument. Jo gairebé li dirà quines són les coses que necessitarem saber el són. Però volem revisar la biblioteca termios. Mai he utilitzat aquesta biblioteca en tota la meva vida, el que té efectes molt mínims. Però això serà la biblioteca es pot utilitzar per rebutjar el caràcter que va colpejar quan s'està escrivint en estàndard polz Així hacker_typer.c, i anem a voler incloure # . Quant a la pàgina del manual de termios - estic endevinant que la terminal US o alguna cosa així - No sé com llegir-lo. Quant a això, es diu per incloure aquests dos arxius, de manera que farem això. Primer el primer, volem prendre en un sol argument, que és l'arxiu que s'ha d'obrir. Llavors, què és el que vull fer? Com puc comprovar que tinc un sol argument? [Estudiant] Si argc és igual. >> [Bowden] Yeah. Així que si (argc = 2!) Printf ("Ús:% s [arxiu per obrir]"). Així que ara si em quedo, sense aportar un segon argument - oh, necessito la nova línia - veuràs que diu ús:. / hacker_typer, i després el segon argument ha de ser el fitxer que voleu obrir. I ara què faig? Vull llegir d'aquest arxiu. Com puc llegir un arxiu? [Estudiant] Vostè obrir primer. Sí >>. Així fopen. Què fopen sembla? [Estudiant] Nom del fitxer. >> [Bowden] Nom de l'arxiu que serà argv [1]. [Estudiant] I llavors, què vols fer amb ell, així que el - >> [Bowden] Yeah. Així que si no s'acordava, es podia fer fopen home, on serà un camí const char * on ruta és nom de fitxer, manera const char *. Si per casualitat vostè no recorda quina manera és així, llavors vostè pot buscar la manera. A l'interior de les pàgines de manual, el caràcter de barra és el que pot utilitzar per buscar les coses. Així que escriure / mode per buscar la manera. ni N són el que vostè pot utilitzar per desplaçar-se per les coincidències de cerca. Aquí es diu que els argument manera apunta a una cadena començant amb una de les seqüències següents. Així que r, arxiu de text obert per a la lectura. Això és el que vull fer. Per llegir, i vull guardar això. La cosa serà un arxiu *. Ara, què és el que vull fer? Dóna'm un segon. Bé. Ara, què és el que vull fer? [Estudiant] Comproveu que és NULL. >> [Bowden] Yeah. Cada vegada que s'obre un arxiu, assegureu-vos que vostè és capaç amb èxit per obrir-lo. Ara vull fer aquestes coses termios on vull llegir primer la configuració actual i salvar aquells en alguna cosa, llavors vull canviar les meves configuracions llençar a les escombraries qualsevol personatge que m'escrigui, i després vull actualitzar aquests valors. I després, al final del programa, vull tornar al meu configuració original. Així que l'estructura serà de termios tipus, i ho vaig a voler-ne dos. La primera d'elles serà la meva current_settings, i després seran meus hacker_settings. En primer lloc, vaig a voler arxivar els paràmetres actuals, llavors vaig a voler actualitzar hacker_settings, i després camí al final del meu programa, vull tornar a la configuració actual. Així que arxivar els paràmetres actuals, la manera com funciona, termios home. Veiem que tenim aquest tcsetattr int, int tcgetattr. Pas en una estructura termios pel seu punter. La manera com es veurà és - HE oblidat i al que la funció va ser cridada. Copiar i enganxar. Així tcgetattr, llavors vull passar en l'estructura que estic estalviant la informació, que serà current_settings, i el primer argument és el descriptor d'arxiu per al que vull per guardar els atributs de. Què és el descriptor de fitxer és com cada vegada que obri un arxiu, s'obté un descriptor d'arxiu. Quan fopen argv [1], s'obté un descriptor de fitxer que es fa referència cada vegada que es vol llegir o escriure-hi. Aquest no és el descriptor de fitxer que voleu utilitzar aquí. Hi ha tres descriptors d'arxiu que té per defecte, que són estàndard, sortida d'estàndard i l'error estàndard. Per defecte, crec que és estàndard en és 0, la sortida estàndard és 1, i l'error estàndard és de 2. Llavors, què és el que vull canviar la configuració de? Vull canviar la configuració de cada vegada que em va tocar un personatge, Vull que llençar aquest caràcter immediat en lloc d'imprimir a la pantalla. Què corrent - estàndard in, out estàndard o error estàndard - respon a les coses quan escric en el teclat? >> [Estudiant] Standard polzades >> Yeah. Així que pot fer de 0 o jo puguem fer stdin. M'estic posant l'estàndard de current_settings polz Ara vull actualitzar aquests valors, el primer que vaig a copiar en hacker_settings el que els meus current_settings són. I com és el treball structs s'acaba de copiar. Això còpia tots els camps, com era d'esperar. Ara vull actualitzar alguns dels camps. Quant a termios, vostè hauria de llegir a través d'una gran quantitat d'aquest només per veure el que vostè vol buscar, però les banderes que vostè va a voler tenir en compte són ressò, així ECHO Echo caràcters d'entrada. En primer lloc vull deixar - HE oblidat i al que els camps són. Això és el que l'estructura s'assembla. Així modes d'entrada Crec que voleu canviar. Veurem la solució per assegurar-se que és el que volem canviar. Volem canviar lflag per tal d'evitar haver de buscar a través de tots aquests. Volem canviar les maneres locals. Vostè hauria de llegir tota aquesta cosa a entendre que tot pertany que volem canviar. Però és a l'interior de les maneres locals on anem a voler canviar això. Així hacker_settings.cc_lmode és el que es diu. c_lflag. Aquí és on ens fiquem en els operadors bit a bit. Estem una mica fora de temps, però anem a anar a través d'ell molt ràpid. Aquí és on ens fiquem en els operadors bit a bit, on crec que vaig dir un cop fa molt de temps que cada vegada de començar a tractar amb banderes, vostè estarà utilitzant l'operador bit a bit molt. Cada bit de la bandera correspon a algun tipus de comportament. Així que aquí, aquest indicador té un munt de coses diferents, on tots ells signifiquen alguna cosa diferent. Però el que vull fer és apagar el bit que correspon a ECHO. Així que per apagar això que faig i = ¬ ECHO. En realitat, crec que és com sostre o alguna cosa així. Vaig a comprovar de nou. Puc termios. És només ECHO. ECHO serà un sol bit. ¬ ECHO voldrà dir que tots els bits estan posats a 1, el que significa que tots els indicadors s'estableixen en true excepte per al bit d'ECHO. En posar fi als meus banderes properes amb això, vol dir totes les banderes que estan establertes actualment en true Encara s'estableix en true. Si la meva bandera ECHO s'estableix en true, llavors aquest és necessàriament estableix false a la bandera ECHO. Així que aquesta línia de codi només s'apaga l'indicador d'ECHO. Les altres línies de codi, només vaig a copiar en l'interès de temps i després explicar-los. En la solució, diu 0. És probable que sigui millor dir explícitament stdin. Recordeu que també estic fent ECHO | ICANON aquí. ICANON es refereix a alguna cosa diferent, és a dir, la manera canònic. Què significa la manera canònic és en general quan s'està escrivint la línia d'ordres, estàndard no processa res fins que arribis nova línia. Així que quan vostè GetString, escriu un munt de coses, llavors vostè colpeja nova línia. Va ser llavors quan s'envia a l'estàndard polz Aquest és el valor predeterminat. Quan desactiveu el mode canònic, ara cada personatge prem és el que es processa, que sol ser una mica malament perquè és lent per processar aquestes coses, pel que és bo per esmorteir en línies senceres. Però vull que cada personatge per ser processat ja que no vull que m'esperis per colpejar de nova línia abans de processar tots els personatges que he estat escrivint. Això desactiva la manera canònic. Aquestes coses només vol dir quan en realitat processa caràcters. Això significa processar immediatament, tan bon punt els estic escrivint, processar-los. I aquesta és la funció que està actualitzant la meva configuració estàndard a dins, i mitjans TCSA fer-ho ara mateix. Les altres opcions són esperar que tot el que es troba actualment en el corrent es processa. Això realment no importa. Just en aquest moment pot canviar les meves configuracions per ser el que actualment es troba en hacker_typer_settings. Suposo que el va anomenar hacker_settings, així que anem a canviar això. Canviar tot per hacker_settings. Ara, al final del nostre programa voldrem tornar al que és en l'actualitat dins de normal_settings, que es veurà igual i normal_settings. Tingueu en compte que no he canviat cap dels meus normal_settings ja que originalment aconseguint. Llavors per simplement canviar de nou, els passa de nou al final. Aquesta va ser l'actualització. Bé. Ara dins d'aquí només explicaré el codi en nom del temps. No és que molt. Veiem, llegim un caràcter de l'arxiu. L'anomenem f. Ara vostè pot l'home fgetc, però com fgetc va a funcionar només es tornarà el caràcter que vostè acaba de llegir o EOF, que es correspon amb el final de l'arxiu o algun succés d'error. Estem bucle, sense deixar de llegir un sol caràcter de les actuacions, fins que ens hem quedat sense caràcters a llegir. I ja que estem fent això, esperem en un sol caràcter de norma polz Cada vegada que escrigui alguna cosa en la línia d'ordres, que està llegint en un personatge de nivell polz Llavors putchar és només posarà el carbó llegim aquí dalt des de l'arxiu de sortida estàndard. Pot putchar home, però és només posar en la sortida estàndard, s'imprimeix aquest caràcter. També pot simplement fer printf ("% c", c); La mateixa idea. Això farà la major part del nostre treball. L'últim que vas a voler fer és fclose nostre arxiu. Si no fclose, això és una pèrdua de memòria. Volem fclose l'arxiu que es va obrir originalment, i crec que això és tot. Si fem això, jo ja tinc problemes. Anem a veure. Què és el que queixar? S'esperava 'int' però l'argument és de tipus 'struct _IO_FILE *'. A veure si això funciona. Només permès a C99. Augh. Està bé, fer hacker_typer. Ara tenim descripcions més útils. Per tant l'ús d'identificador no declarat "normal_settings '. Jo no el va anomenar normal_settings. El vaig trucar current_settings. Així que anem a canviar tot això. Ara passa argument. Vaig a fer això 0 per ara. Bé. . / Hacker_typer cp.c. Jo també no esborrar la pantalla al principi. Però vostè pot mirar cap enrere al conjunt últim problema per veure com netejar la pantalla. És simplement imprimir alguns caràcters mentre que això està fent el que vull fer. Bé. I pensant en per què això havia de ser 0 en lloc de l'entrada estàndard, que ha de ser # defineix 0, aquest es queixa que - Abans, quan he dit que no hi ha descriptors d'arxiu, però després també tens el teu * FILE, un descriptor d'arxiu és només un únic nombre enter, mentre que un FILE * té un munt de coses associada a ella. La raó per la qual hem de dir 0 en lloc de l'entrada estàndard és que stdin és un FILE * que apunta al que fa referència descriptor d'arxiu 0. Així que fins aquí quan faig fopen (argv [1], m'estic posant un * fitxer. Però en algun lloc on * FILE és una cosa que correspon al descriptor d'arxiu per a l'arxiu. Si ens fixem en la pàgina del manual de obert, així que crec que hauré de fer man 3 obert - No - man 2 obert - si. Si ens fixem en la pàgina d'obert, obert és com un fopen de nivell inferior, i es torna el descriptor d'arxiu real. fopen fa un munt de coses sobre obert, que en lloc de tornar només el descriptor de fitxer retorna un arxiu complet punter * dins dels quals és el nostre descriptor d'arxiu petit. Així estàndard es refereix a la cosa * FILE, mentre que 0 es refereix només a la norma descriptor de fitxer en si mateix. Preguntes? [Rialles] va bufar a través d'això. Està bé. Hem acabat. [Rialles] [CS50.TV]