[Powered by Google Translate] [Sección 5 - máis cómodo] [Rob Bowden - Harvard University] [Esta é CS50. - CS50.TV] Como dixo no meu correo electrónico, hai unha morea de cousas que podes usar Ademais do aparello de realmente facer os conxuntos de problemas. Recomendamos que faga iso no aparello só porque entón nós podemos axudar con máis facilidade e sabemos como todo vai funcionar. Pero, como un exemplo de onde pode facer cousas, digamos, se non ten acceso para un dispositivo ou quere traballar no soto do Centro de Ciencias - que en realidade teñen o aparello tamén - se quere traballar en calquera lugar. Un exemplo é xa viu / escoitou falar de SSH? O SSH é basicamente como conectarse a algunha cousa. En realidade, agora eu estou SSHed no aparello. Eu nunca traballar directamente no aparello. Aquí está o aparello, e se ollar para abaixo aquí ve este enderezo IP. Eu nunca traballar no propio aparello; Eu sempre viña dunha xanela xanela de terminal / iTerm2. Pode SSH para que o enderezo IP, ssh jharvard@192.168.129.128. Lembro que ese número moi facilmente porque é un estándar tan agradable. Pero iso me vai pedir o meu contrasinal, e agora eu estou no aparello. Basicamente, neste momento, se abriu un terminal dentro do propio dispositivo, esta interface, pero usalo, é exactamente a mesma como a interface que está a usar aquí, pero agora está SSHed. Non ten que SSH para o aparello. Un exemplo de outro lugar podería SSH é que eu estou seguro que ten por defecto - Oh Maior. Todos vostedes deben ter por contas estándar FAS nos servidores da FAS. Para min, eu faría o SSH para rbowden@nice.fas.harvard.edu. Vai pedirlle que a primeira vez, e di que si. O meu contrasinal é só vai ser o meu contrasinal fans. E agora, eu estou SSHed aos servidores agradable, e podo facer o que eu queira aquí. Unha morea de clases que poden ser adoptadas, como a 124, vai ter que cargar cousas para aquí para realmente presentar os seus conxuntos de problemas. Pero digamos que non ten acceso ao seu dispositivo. Entón podes facer cousas, como aquí, que vai dicir - Este é só nosa sección de preguntas. El vai pedir para facer iso no aparello. En vez diso eu vou facelo no servidor. Eu estou indo para descomprimir iso. O problema vai ser que está acostumado a usar algo como gedit ou o que quere no interior do aparello. Non vai ter que o servidor fans. É todo só vai ser esta interface textual. Entón, vostede podería un ou outro, tratar de aprender un editor de texto que eles teñen. Teñen Nano. Nano é normalmente moi doado de usar. Pode usar as súas frechas e escriba normalmente. Entón, iso non é difícil. Se queres comezar realmente a fantasía que pode usar o Emacs, que probablemente non debería ter aberto porque eu non sei nin como pechar Emacs. Control X, Control C? Si Ou pode utilizar o Vin, que é o que eu uso. E así esas son as súas opcións. Se non queres facer iso, tamén se pode, se ollar para manual.cs50.net - Oh Nun PC, pode SSH usando putty, que vai ter que baixar por separado. En un Mac, pode só polo terminal de uso estándar ou podes baixar iTerm2, que é como un terminal, agradable fantasía. Se vai para manual.cs50.net podes ver un link para o Notepad + +, que é o que pode usar en un PC. El permite que SFTP de Notepad + +, que é basicamente SSH. O que isto vai deixar facer é editar os seus arquivos locais, e sempre que quere garda-los, el ha salvar a nice.fas, onde podes executa-los. É o equivalente en un Mac vai ser TextWrangler. Entón, el permite que fai a mesma cousa. El permite que edite arquivos localmente e garda-los en nice.fas, onde podes executa-los. Entón, se está sempre preso sen un aparello, ten estas opcións aínda facer os seus xogos de problemas. O único problema vai ser que non vai ter a biblioteca CS50 porque nice.fas non por defecto ten iso. Podes baixar a biblioteca CS50 - Eu non creo que eu teño neste momento. Podes baixar a biblioteca CS50 e copia-lo para nice.fas, ou eu creo que neste momento non usalo máis de calquera maneira. Ou nós, pode, polo de agora, substituílo por as implementacións das funcións na biblioteca CS50 de calquera maneira. De xeito que non debe ser moi dunha restrición. E é iso. Vou volver para o aparello agora, nós imos facer todo o aparello. Mirando para a nosa sección de preguntas, en principio, como dixen no meu correo electrónico, temos que falar do curto debería asistir. Temos a redirección e Pipes e esas tres preguntas. Para que fluxo que funciona como printf escribir por defecto? Entón córrego. ¿Que é un fluxo? Un fluxo é basicamente como se fose só algúns - Non é mesmo unha fonte de 1s e 0s. O fluxo é pedir aquí é a estándar. E así por defecto é un fluxo que cando escribir para el, aparece na pantalla. A Standard, por stream, isto significa que acaba de escribir 1s e 0s a el, E a outra extrema de saída estándar só le a partir dese fluxo. É só unha secuencia de 1s e 0s. Podes escribir para córregos ou pode ler a partir de fluxos dependendo do que o fluxo é realmente. Os outros dous fluxos estándar son estándar e erro estándar. Estándar en sempre que GetString, está esperando por ti para material de entrada. Por iso, esperando por ti, é realmente esperando por defecto en, o que é realmente o que comeza cando escribe no teclado. Vostede está escribindo en estándar dentro Erro estándar é basicamente equivalente á saída estándar, pero está especializada en que cando imprimir para o erro estándar, debería só imprimir mensaxes de erro para que para que poida distinguir entre mensaxes regulares impresos na pantalla contra mensaxes de erro, dependendo se eles foron para fóra estándar ou erro estándar. Arquivos tamén. Fóra estándar, patrón, e erro estándar son fluxos só especiais, pero realmente calquera arquivo, cando abre un arquivo, el torna-se un fluxo de bytes onde podes só ler este fluxo. Vostede, no seu maior parte, pode só pensar nun ficheiro como un fluxo de bytes. Entón, o que os fluxos que escriben para por defecto? Saída estándar. Cal é a diferenza entre> e? >> Será que alguén asistir ao vídeo antes? Okay. > Vai ser como redireccionar en arquivos, e >> tamén vai redireccionar a saída en arquivos, pero é en vez de ir para engadir o ficheiro. Por exemplo, digamos que eu ocorrer de ter Dict aquí, e as cousas só dentro Dict é gato, gato, can, peixe, can. Un comando que ten na liña de comandos é gato, que está indo só para imprimir o que está nun arquivo. Polo tanto, cando digo Dict gato, que vai imprimir gato, can, gato, peixe, can. Isto é todo o gato fai. Isto significa que impresa na saída estándar, gato, can, gato, peixe, can. Se eu xa quere redireccionar iso para un arquivo, podo usar>, redireccionándoos para o que é o ficheiro. Vou chamar o ficheiro de arquivo. Entón agora eu ls, eu vou ver eu teño un novo ficheiro chamado ficheiro. E se eu abri-lo, el vai ter exactamente o gato poñer na liña de comandos. Polo tanto, agora se eu fai iso de novo, entón vai redireccionar a saída en arquivo, e eu vou ter exactamente a mesma cousa. Entón, tecnicamente, é completamente anulou o que tiñamos. E imos ver se eu cambiar Dict, eu tirei can. Agora, se nós gato Dict en arquivo novo, nós imos ter a nova versión co can eliminado. Entón está completamente substitúe-lo. En vez diso, se usarmos >>, que vai engadir o ficheiro. Agora, abra o ficheiro, vemos que temos exactamente a mesma cousa dúas veces impresa porque alí unha vez máis, a continuación, engade ao orixinal. Entón é iso que >>> e facer. Será que a seguinte pregunta - Non preguntar sobre iso. O outro que temos é <, que se> redirecciona saída estándar, fai 2>, que está redirixido erro estándar. Entón, se algo deu o erro estándar, non tería colocado en txt2. Pero repare se eu fai 2>, entón aínda está imprimindo Ola, Rob! para a liña de comandos porque eu estou só redirixido erro estándar, eu non estou redirixido a saída estándar. Erro estándar e saída estándar son diferentes. Se quixese realmente escribir para o erro estándar, entón eu podería cambiar isto para fprintf para stderr. Entón printf, por defecto, imprime para a saída estándar. Se eu queira imprimir a erro estándar man, entón eu teño que usar fprintf e especificar o que quere imprimir. Se en vez fixen saída estándar fprintf, a continuación, que é basicamente equivalente a printf. Pero fprintf para o erro estándar. Entón, agora, se eu redireccionar isto txt2, Ola, Rob! aínda está a ser impresas na liña de comandos unha vez que está a ser impresas para o erro estándar e eu só estou redirixido a saída estándar. Se eu agora redireccionar o erro estándar, agora non se impreso, e txt2 será Ola, Rob! Entón, agora, pode imprimir os seus erros reais para o erro estándar e imprimir as súas mensaxes regulares para a saída estándar. E así, cando executar o programa, pode executa-lo como. / Ola, este tipo co 2> para que o seu programa vai funcionar normalmente, pero as mensaxes de erro que comeza pode comprobar máis tarde, no seu rexistro de erro, así os erros, e logo mirar máis tarde eo seu arquivo de erros terá posibles erros que se produciron. Preguntas? A última é a tubulação, que pode pensar en como aproveitar a saída estándar dun comando e tornándose o patrón do comando seguinte. Un exemplo aquí é o eco é unha cousa de liña de comandos que é só ir a ecoar o que eu coloque como o seu argumento. Non vou poñer comiñas. Eco bla, bla, bla é só ir para imprimir bla, bla, bla. Antes, cando dixo que eu tiña que poñer Rob nun arquivo txt porque eu só podo redireccionar os arquivos txt, en vez diso, / se eu echo Rob e despois canaliza-lo. / Ola, que tamén vai facer o mesmo tipo de cousas. Isto está levando a saída deste comando, eco Rob, e usalo como entrada. / Olá Podes pensar niso como primeira redirección eco Rob nun arquivo e despois da entrada en. / Ola, este arquivo que acaba de ser emitidas. Mais é preciso o ficheiro temporal fóra do cadro. Preguntas sobre iso? A seguinte pregunta vai implicar iso. O gasoduto podería usar para atopar o número de nomes exclusivos nun arquivo chamado names.txt? Os comandos que imos querer usar aquí son únicos, por iso uniq, e WC. Podes facer uniq home para realmente ollar para o que fai, e el só vai para filtrar liñas correspondentes adxacentes a partir da entrada. E o home baño vai imprimir a palabra, salto de liña, e máis para cada ficheiro. E a última que imos querer usar é tipo, que está indo só para clasificar liñas dun ficheiro txt. Se eu fai un arquivo txt, names.txt, e Rob, Tommy, Joseph, Tommy, Joseph, RJ, Rob, o que quero facer aquí é atopar o número de nomes únicos neste arquivo. Entón, o que debe ser a resposta? >> [Alumno] 4. Si >>. Debe ser 4, pois Rob, Tommy, Joseph, RJ son os nomes orixinais só neste arquivo. O primeiro paso, se eu só facer reconto de palabras na names.txt, é dicir, en realidade, me contando todo. Esta é realmente a impresión - Imos ver, o home baño - novas liñas, palabras e contador de bytes. Se eu só me importa as liñas, entón podo só facer baño-l names.txt. Entón ese é un paso. Pero eu non quero baño-l names.txt porque names.txt só contén todos os nomes, e quero filtrar as non orixinais. Entón, se eu fai names.txt uniq, que non chega a me dar o que quero porque os nomes duplicados aínda están alí. Por que isto? Por que non uniq facendo o que quero? [Alumno] O dobre non son [inaudível] >> Yeah. Teña en conta que a páxina de manual para uniq di filtrar liñas correspondentes adxacentes. Eles non son adxacentes, polo que non pode filtra-los. Se eu clasificalos en primeiro lugar, names.txt tipo vai poñer todas as liñas duplicadas xuntos. Entón agora é que tipo names.txt. Vou querer usar isto como a entrada uniq, que é | uniq. Iso dáme José, RJ, Rob, Tommy, e quero usar isto como a entrada para o baño-l, que me vai dar 4. Como di aquí, o gasoduto podería usar? Podes facer unha chea de cousas como usar unha serie de comandos onde se utiliza a saída dun comando como entrada para o próximo mando. Podes facer unha chea de cousas, unha morea de cousas intelixentes. Preguntas? Okay. Isto é todo para tubos e redirección. Agora imos para a cousa real, o material de codificación. Dentro deste PDF, podes ver este comando, e quere executar este comando no seu dispositivo. wget é o comando para só incorporarse algo de Internet, basicamente, así wget e este URL. Se foi para este URL no seu navegador, sería baixar o arquivo. Eu só facer clic sobre el, por iso a descarga do ficheiro para min. Pero escribir wget de que a cousa dentro do terminal é só ir a baixa-lo no seu terminal. Eu teño section5.zip, e queres descomprimir section5.zip, que vai dar-lle unha carpeta chamada section5, que vai ter todos os arquivos que imos estar usando hoxe dentro del. Como os nomes destes programas de arquivo suxerir, son un pouco buggy, así que a súa misión é descubrir por que usar gdb. Será que todos eles teñen baixado / saber como se atope los baixo no seu dispositivo? Okay. Correndo ./buggy1, que vai dicir fallo de segmento (núcleo), que en calquera momento comeza un segfault, é unha cousa mala. Baixo que circunstancias comeza un segfault? [Alumno] desreferenciação un punteiro nulo. Si >>. De xeito que é un exemplo. Dereferencing un punteiro nulo está indo para obter un segfault. O que un segfault significa que está tocando memoria que non debe tocar. Entón dereferencing un punteiro nulo é tocar o enderezo 0, e, basicamente, todos os ordenadores hoxe en día dicir que 0 enderezo é a memoria que non debe tocar. É por iso que unha dereferencing resultados punteiro nulo nun segfault. Cando ocorrer de non iniciar un punteiro, el ten un valor de lixo, e así, cando tenta eliminar a referencia que, con toda probabilidade que está tocando memoria que está no medio da nada. Se ocorrer de ter sorte e do valor de lixo pasou para apuntar a algún lugar na pila ou algo así, entón cando dereference que o punteiro que non lle inicializar, nada vai dar mal. Pero se está a apuntar cara, digamos, en algún lugar entre a pila eo heap, ou está a apuntar só para algún lugar que non foi utilizado polo seu programa, sen embargo, entón está tocando memoria que non debe estar tocando e segfault. Cando escribir unha función recursiva e recurses moitas veces ea súa pila crece moi grande e as colide pila en cousas que non debe ser colidindo con, está tocando memoria que non debe tocar, así segfault. Isto é que é segfault. É tamén a mesma razón que se ten unha secuencia como - imos volver ao programa anterior. En hello.c-estou indo só para facer outra cousa. char * s = "Ola mundo!"; Se eu usar * s = algo ou s [0] = 'X'; entón faga Ola,. / Ola, por que iso segfault? Por que isto segfault? O que espera que aconteza? Se eu fixen printf ("% s \ n" s), o que espera para ser impreso? [Estudante] X Olá Si >>. O problema é que cando declarar unha secuencia como esta, s é un punteiro que está indo a ir a pila, e que s está destacar é esta corda que está contido na memoria só de lectura. Entón, só pola memoria o nome, só de lectura, ten que ter a idea que se tentar cambiar o que está na memoria só de lectura, está facendo algo que non debería estar facendo a memoria e segfault. Esta é realmente unha gran diferenza entre char * s e s char []. Entón char s [], agora esta cadea será colocado na pila, ea pila non é só de lectura, o que significa que este debe funcionar perfectamente ben. E el fai. Lembre que cando fago char * s = "Ola mundo!", S en si é na pila s puntos, pero en outro lugar, en outro lugar e que pasa a ser de só lectura. Pero char s [] é só algo sobre a pila. Entón, iso é outro exemplo dun segfault suceder. Vimos que ./buggy1 resultou nun segfault. En teoría, non debe ollar para buggy1.c inmediatamente. En vez diso, nós imos ollar para el a través gdb. Teña en conta que cando comeza un fallo de segmentación (núcleo), tes este arquivo sobre o núcleo aquí chamado. Se ls-l, imos ver que o núcleo xeralmente é un arquivo moi grande. Este é o número de bytes do arquivo, por iso parece que é 250 e poucos kilobytes. A razón para isto é que o despejo do núcleo, en realidade, é é cando as súas fallos do programa, o estado da memoria do seu programa só é copiado e pegado a este ficheiro. El é despejada dentro do arquivo. Este programa, mentres estaba correndo, pasou a ter un uso de memoria de preto de 250 kilobytes, e iso é o que ten despexado neste arquivo. Agora podes ollar para o arquivo, se facemos gdb núcleo buggy1. Podemos só facer o gdb buggy1, e que só vai comezar a gdb regularmente, usando buggy1 como o seu ficheiro de entrada. Pero se fai gdb núcleo buggy1, entón é especialmente indo para iniciar gdb mirando o arquivo do núcleo. E dicindo buggy1 gdb medio sabe que o arquivo vén de núcleo do programa buggy1. Entón gdb buggy1 núcleo vai traer connosco inmediatamente onde o programa pasou a rematar. Vemos aquí programa rematou con sinal de 11, fallo de segmento. Nós ocorrer para ver unha liña de montaxe, que probablemente non é moi útil. Pero se escribir BT ou backtrace, que vai ser a función que nos dá a lista dos nosos cadros de pila actuais. Entón backtrace. Parece que só temos dous cadros de pila. O primeiro é o noso cadro de pila principal, eo segundo é o cadro de pila para esta función que acontecer para ser, que parece que só temos o código de montaxe para. Entón, imos voltar a nosa función principal, e para iso podemos facer un cadro, e creo que tamén pode facerse, pero eu case nunca cara abaixo - ou cara arriba. Si Arriba e abaixo. Up leva ata un cadro de pila, para abaixo leva a un cadro de pila. Teño tendencia a non usar isto. Eu só dicir especificamente o cadro 1, que é volver para o cadro co 1. Cadro 1 vai traer-nos en cadro de pila principal, e el di que aquí a liña de código que esteamos menos. Se quixésemos liñas máis un par de código, podemos dicir lista e que vai dar todas as liñas de código ao seu redor. A liña que foi segfaulted en 6: if (strcmp ("CS50 rochas", argv [1]) == 0). Se non é obvio aínda, pode obterse directamente aquí só pensar por que segfaulted. Pero podemos dar un paso máis lonxe e dicir: "Por que argv [1] segfault?" Imos impresión argv [1], e parece que é 0x0, que é o punteiro nulo. Estamos strcmping CS50 rochas e nulos, e así que vai segfault. E por que é argv [1] nulo? [Alumno] Porque non dar calquera argumentos de liña de comandos. Si Non dar os argumentos de liña de comandos. Entón ./buggy1 só vai ter argv [0] ser ./buggy1. Non vai ter unha argv [1], de xeito que vai segfault. Pero, en vez diso, eu só CS50, que vai dicir que comeza un D porque iso é o que é suposta facer. Mirando buggy1.c, suporía para imprimir "Ten un D" - Se argv [1] non é "CS50 rochas", "Ten un D", máis "gañou un!" Polo tanto, se queremos un A, temos que para comparar como certa, o que significa que compara a 0. Entón, argv [1] que ser "CS50 pedras". Se queres facer iso na liña de comandos, ten que usar \ para escapar do espazo. Entón CS50 \ rochas e gaña un! Se non fai a barra invertida, porque isto non funciona? [Estudante] e dous argumentos diferentes. Si >>. Argv [1] vai ser CS50, e argv [2] vai ser rochas. Okay. Agora ./buggy2 vai segfault novo. En vez de abri-lo co seu arquivo de núcleo, imos abrir buggy2 directamente, así gdb buggy2. Agora, se nós simplemente executar o noso programa, entón el vai dicir Programa recibiu sinal SIGSEGV, que é o segfault sinal, e este é o lugar onde se produciu a acontecer. Mirando para o noso backtrace, vemos que estabamos no oh_no función, que foi chamado polo Dinky función, que foi chamado polo binky función, que foi chamado por principal. Tamén podemos ver os argumentos para estas funcións. O argumento para Dinky e binky foi 1. Se incluír a función oh_no, vemos que oh_no está só facendo char ** s = NULL; * S = "boom"; Por que isto non? [Alumno] Non pode cancelar o punteiro nulo? Si >>. Este é só dicir s NULL, independentemente de se isto acontece de ser un char **, o que, dependendo da forma como o interpretar, que podería ser un enlace a un enlace a unha cadea ou unha matriz de cadeas. É s NULL, entón * s é dereferencing un punteiro nulo, e así é que isto vai funcionar. Esta é unha das formas máis rápidas que pode posiblemente segfault. É só declarar un punteiro nulo e inmediatamente segmento estándar. Isto é o que está facendo oh_no. Ou tamén ata un cadro, entón imos entrar na función que chamou oh_no. Eu teño que facer iso para abaixo. Se non escribir un comando e só prema Intro novamente, el vai só repetir o mando anterior que foi. Estamos no cadro 1. Listado este cadro, vemos aquí é a nosa función. Podes bater lista de novo, ou pode facer lista de 20 e ha incluír máis. O Dinky función di que se eu for 1, entón vai para a función oh_no, senón vai para a función furtivo. E sabemos que é un i porque pasou de verse aquí Dinky que foi chamado o argumento 1. Ou pode simplemente non imprimir I e vai dicir que eu é 1. Estamos actualmente en Dinky, e subir outro cadro, sabemos que vai acabar en binky. Up Agora estamos en binky. Listado esta función - a lista de antes de media me cortou - empezou como se eu é 0, entón imos chamalo oh_no, senón chamar Dinky. Sabemos que eu era un, por iso chamado Dinky. E agora estamos de volta no inicio, e principal só vai ser int i = rand ()% 3; Isto só lle vai dar un número aleatorio que é 0, 1 ou 2. Vai chamar binky con ese número, e ha voltar 0. Mirando para iso, só camiñando a través do programa manualmente, sen executa-lo inmediatamente, ten que definir un punto de ruptura na principal, o que significa que, cando executar o programa seu programa é executado ata que chega a un punto de ruptura. Así, a execución do programa, será executado e logo vai bater a función principal e deixar de funcionar. Agora estamos dentro da principal, e paso ou a próxima vai traer-nos para a próxima liña de código. Podes facer a paso ou o próximo. Bater seguinte, agora eu fose definido para Rand () do 3%, de modo que pode imprimir o valor de i, e el vai dicir que eu é 1. Agora, non importa se usan preto ou paso. Eu creo que importaba o anterior, pero quere usar a continuación. Se usarmos paso, paso para a función, o que significa ollar para a cousa real que está a suceder dentro binky. Se usarmos seguinte, entón isto significa pasar por riba da función e vaia á seguinte liña de código na nosa función principal. Aquí nesta liña, eu estaba onde dixo Rand ()% 3; se eu paso, ía para a implementación de rand e ollar para o que está a suceder alí, e eu podería pisar a través da función rand. Pero eu non me importa a función rand. Eu só quero ir á seguinte liña de código na principal, entón eu uso seguinte. Pero agora eu me importa a función binky, así que quero entrar niso. Agora estou en binky. A primeira liña de código que vai dicir se (i == 0), dou un paso, vemos que acabar en Dinky. Se as cousas nos da lista, vemos que é marcada i = 0. i non é igual a 0, de modo que foi a condición máis, que vai chamar Dinky (i). Pode ficar confuso. Se só ollar para estas liñas directamente, pode pensar que se (i == 0), ok, entón eu dei un paso e agora estou en Dinky (i), pode pensar que debe significar i = 0 ou algo así. Non Significa só que el sabe que pode ir directamente ao Dinky liña (i). Porque eu non for 0, o seguinte paso non vai rematar no outro. Else non é unha liña que vai parar. É só pasar para ir á seguinte liña realmente pode facer, o que é atractivo (i). Entrando Dinky (i), imos ver se (i == 1). Sabemos i = 1, entón cando paso, sabemos que imos terminar en oh_no porque i = 1 chámase o oh_no función, que pode entrar, que vai definir char ** s = a NULL e inmediatamente "boom". E entón, en realidade, ollando para a implementación de buggy2, iso, eu só está obtendo un número aleatorio - 0, 1 ou 2 - chamado binky, que se eu for 0 ela chama oh_no, senón el chama Dinky, que vén ata aquí. Se eu fose 1, chamada oh_no, senón chamar slinky, que chegando aquí, i é 2, chame oh_no. Eu nin creo que hai un camiño - Alguén ve unha forma de facelo dun programa que non vai segfault? Porque a menos que eu estou falta algo, se for 0, vostede inmediatamente segfault, outra cousa que ir a unha función que se i é un que segfault, outra cousa que ir a unha función onde se i é 2, segfault. Polo tanto, non importa o que fai, ten segfault. Eu creo que unha forma de resolve-lo sería en vez de facer char ** s = NULL, podería malloc espazo para esa cadea. Nós poderiamos facer malloc (sizeof) - sizeof o que? [Alumno] (char) * 5? >> Isto parece certo? Estou supoñendo que iso vai funcionar se eu realmente el foi, pero non é o que eu estou buscando. Olle para o tipo de s. Imos engadir * int, entón int * x. Eu faría malloc (sizeof (int)). Ou se eu quería un conxunto de cinco, eu faría (sizeof (int) * 5); E se eu tivera un ** int? O que eu malloc? [Estudante] Tamaño do punteiro. Si >>. (Sizeof (int *)); A mesma cousa aquí. Eu quero (sizeof (char *)); Isto vai reservar espacio para o punteiro que apunta a "boom". Eu non teño reservar espacio para "boom" en si porque este é basicamente equivalente ao que eu dixen antes de char * x = "boom". "Boom" xa existe. Acontece que hai na rexión de lectura de memoria. Pero xa existe, o que significa que esta liña de código, se s é un char **, entón * s é un char * e está definindo este char * para apuntar para "boom". Se eu quería copiar "boom" en s, entón eu tería que reservar espazo para s. Vou facer * s = malloc (sizeof (char) * 5); Por 5? Por que non catro? Parece que o "boom" é de 4 caracteres. >> [Alumno] O personaxe nulo. Si Todas as cordas van precisar o carácter nulo. Agora podo facer algo como strcat - Cal é a función para copiar a cadea? [Alumno] cpy? >> Strcpy. strcpy home. Entón strcpy ou strncpy. strncpy é un pouco máis seguro, pois pode especificar exactamente cantos caracteres pero aquí non importa, porque sabemos. Entón strcpy e ollar nos argumentos. O primeiro argumento é o noso destino. O segundo argumento é a nosa fonte. Imos copiar para o noso destino * é o punteiro "boom". Por que quere facelo cun strcpy en vez de só o que tiñamos antes de * s = "boom"? Hai unha razón para que pode querer facelo, pero o que é iso? [Estudante] Se quere cambiar algo en "boom". Si >>. Agora podo facer algo como s [0] = 'X'; porque os puntos s para a pila e que o espazo na pila que está a apuntar cara s é un punteiro para máis espazo na pila, que está almacenando "boom". Polo tanto, este exemplar de "boom" está sendo almacenado na pila. Hai técnicamente dúas copias do "boom" no noso programa. Non é o primeiro que é só dada por esta constante cadea "boom", ea segunda copia de "boom", strcpy creou a copia de "boom". Pero a copia de "boom" está sendo almacenado na pila, eo heap está libre de cambiar. A pila non é só de lectura, o que significa que s [0] vai deixar que cambiar o valor de "boom". Vai deixar lo pasar eses personaxes. Preguntas? Okay. Pasando para buggy3, gdb imos buggy3. Nós só executa-lo e vemos temos un segfault. Se backtrace, hai só dúas funcións. Subir na nosa función principal, vemos que segfaulted nesta liña. Entón, só de ollar para esta liña, é (int liña = 0; fgets este material fai NULL non é igual; liña + +). O noso cadro anterior foi chamado _IO_fgets. Vai ver que unha morea de funcións built-in C, que cando comeza a segfault, haberá nomes de funcións realmente enigmáticas como este _IO_fgets. Pero que vai relacionarse con esta chamada fgets. En algún lugar aquí dentro, estamos segmento estándar. Se olharmos para os argumentos para fgets, podemos imprimir buffer. Imos imprimir como un - Oh, non. Impresión non vai funcionar exactamente como quero que el. Imos ollar para o programa real. Buffer é unha matriz de caracteres. É unha matriz de caracteres de 128 caracteres. Polo tanto, cando digo buffer de impresión, que vai imprimir os 128 caracteres, Eu creo que é o que se espera. O que eu estaba a buscar e imprimir o enderezo do buffer, pero que realmente non me di moito. Entón, cando ocorrer de eu dicir aquí tapón x, el me amosa 0xbffff090, que, se lembrar de máis cedo ou nalgún punto, Oxbffff tende a ser unha rexión pila-ISH. A pila tende a comezar en algún lugar só baixo 0xC000. Só de ver ese enderezo, sei que o buffer está a suceder na pila. Reiniciar o meu programa, executado, para arriba, o buffer que vimos foi esa secuencia de caracteres que son moito sentido. A continuación, imprimir arquivos, o que arquivo se parece? [Alumno] nulo. Si >>. Arquivo é un tipo FILE *, por iso é un punteiro, e o valor punteiro que é nula. Entón fgets vai ler que o punteiro de forma indirecta, mais, para acceder a este punteiro, ten que dereference-lo. Ou, para acceder ao que debe estar apuntando para el dereferences el. Entón é dereferencing un punteiro nulo e segfaults. Eu podería ter reiniciar alí. Se romper o noso punto principal e executar, a primeira liña de código char * filename = "nonexistent.txt"; Isto debe dar unha información moi grande respecto a porque este programa falla. Escribindo próxima trae-me á seguinte liña, onde abrir este ficheiro, e entón eu inmediatamente entrar na nosa liña, onde unha vez eu bati seguinte, que vai segfault. Alguén quere tirar unha razón pola cal se pode segmento defecto? [Alumno] O ficheiro non existe. Si >>. Isto é suposta ser unha información que sempre que está abrindo un arquivo que precisa para comprobar se o ficheiro realmente existe. Entón, aquí, "nonexistent.txt"; Cando nome fopen para lectura, entón necesitamos dicir if (arquivo == NULL) e dicir printf ("O ficheiro non existe!" ou - mellor aínda - filename); return 1; Entón agora imos comprobar para ver se é NULL antes de realmente seguir e intentar ler a partir dese arquivo. Podemos refacelo lo só para ver o que funciona. Eu pretendía incluír unha nova liña. Entón agora nonexistent.txt non existe. Ten que sempre comprobar se hai este tipo de cousas. Ten que sempre comprobar fopen retorna NULL. Ten que sempre comprobar para asegurarse de que malloc non retorna NULL, ou ben segfault. Agora buggy4.c. Execución. Estou descubrindo que está á espera para entrada ou looping posiblemente infinito. Si, é looping infinito. Entón buggy4. Parece que estamos en loop infinito. Podemos romper a principal, executar o noso programa. En gdb, mentres a abreviatura empregada é inequívoca ou abreviações especiais que fornecen para ti, entón podes usar n empregar esta en vez de ter que escribir lado todo o camiño. E agora que eu bati n unha vez, podo só prema Intro para continuar próximo en vez de ter que bater n Intro, n Intro, n Intro. Parece que eu estou en algún tipo de loop que está asentado matriz [i] a 0. Parece que eu non estou saír deste bucle. Se eu imprimir i, entón eu é 2, entón eu vou ir. Vou imprimir i, i é 3, entón eu vou ir. Vou imprimir i e i é 3. A continuación, imprimir i, i é 4. En realidade, a impresión sizeof (array), así o tamaño da matriz é 20. Pero parece que hai algún comando gdb especial para ir ata que algo acontece. É como definindo unha condición de que o valor da variable. Pero eu non me lembro o que é. Entón, se nós seguir - O que estaba dicindo? O que trae? [Alumno] non ver eu engadir - >> Yeah. Entón amosar podo axudar. Se só amosar i, que vai poñer-se aquí que o valor de i é entón eu non teño que imprimir lo cada vez. Se só vai manter próximo, vemos 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5. Algo está terriblemente mal, e eu está a ser reposto a 0. Mirando buggy4.c vemos todo o que pasa é matriz int [5]; é (i = 0; i <= sizeof (array); i + +) array [i] = 0; O que vemos que está mal aquí? Como unha información, cando estaba facendo o gdb buggy4 - imos romper principal, run - Eu tiña impresión sizeof (array) só para ver o que a condición é onde eu debería finalmente saír. Onde estou? Será que eu corro? Eu non declarou aínda. Así imprimir sizeof (matriz) e que é 20, que é esperado dende a miña matriz é de tamaño 5 e é de 5 números enteiros, así que a cousa toda debe ser de 5 * sizeof (int) bytes, onde sizeof (int) tende a ser 4. Entón, sizeof (array) é 20. O que debe ser iso? [Alumno] dividido por sizeof (int). >> Si, / sizeof (int). Parece que aínda hai un problema aquí. Eu creo que iso debe ser só < xa que é practicamente sempre > [Bowden] Si Cando imos máis aló do final da nosa matriz, espazo que de algunha maneira que estamos substituíndo está substituíndo o valor de i. E así, se olharmos para buggy4, romper carreira, principal, imos imprimir o enderezo de i. Parece que é bffff124. Agora imos imprimir o enderezo do array [0]. 110. E sobre [1]? 114. [2], 118. 11c, 120. array [5] é bfff124. Entón matriz [5] ten o mesmo enderezo como eu, o que significa que a matriz [5] é i. Se eles teñen o mesmo enderezo, son a mesma cousa. Entón, cando imos definir matriz [5] a 0, estamos establecendo i a 0. E se pensar sobre iso en termos de pila, int i é declarada primeiro, o que significa que está un pouco de espazo na pila. Entón matriz [5] é asignado, polo que 20 bytes son alocados na pila. Entón eu é alocada en primeiro lugar, a continuación, eses 20 bytes estar asignado. Entón eu acontece antes de matriz, e por mor da forma como eu dixen a semana pasada, cando técnicamente a pila crece cara abaixo, cando o índice de ti nunha matriz, temos a garantía de que a posición 0 na matriz sempre acontece antes da primeira posición na matriz. Este é o tipo de como eu deseño a semana pasada. Teña en conta que no fondo temos o enderezo 0 e na parte superior temos Max enderezo. A pila está sempre crecendo cara a abaixo. Imos dicir que reservar i. Alocamos enteiro i, o que significa que imos só dicir que ata aquí enteiro i é alocada. A continuación, alocamos noso array de 5 enteiros, o que significa que, por baixo que, Visto que a pila está crecendo para abaixo, eses cinco enteiros quedan alocados. Pero por mor de como matrices funciona, está garantido que a primeira posición na matriz sempre ten un enderezo de menos que o segundo aspecto da matriz. Así posición 0 matriz sempre ten que ocorrer primeiro na memoria, Considerando que a posición matriz 1 ten que acontecer despois diso e posición da matriz 2 ten que acontecer despois diso, o que significa que a posición 0 matriz ía ocorrer en algún lugar aquí, posición matriz 1 que acontecería arriba que porque mover-se significa enderezos máis altos desde que a dirección máxima é aquí. Entón array [0] aquí, array [1] ata aquí, array [2] ata aquí, matriz [3] ata aquí. Observe como antes alocados enteiro i todo o camiño ata aquí, a medida que avanzamos cada vez máis na nosa matriz, estamos chegando máis preto e máis preto do noso enteiro i. O que pasa é que a matriz [5], que é unha posición máis alá da nosa matriz, é exactamente onde enteiro i pasou a ser alocado. Entón ese é o momento en que se celebrará a bater o espazo na pila que foi alocada para o enteiro i, e estamos definindo que a 0. É así que funciona. Preguntas? Si [Alumno] non importa. Okay. [Alumno] Como evitar este tipo de erros? Estes tipos de erros? Non use C como linguaxe de programación. Usar a linguaxe que ten límites de matriz de verificación. Mentres está atento, só precisa evitar ir alén dos límites da súa matriz. [Alumno] Entón aquí cando fomos pasado dos límites da súa matriz - [Bowden] É aí onde as cousas comezan a dar mal. >> [Alumno] Ah, ok. Mentres lle queda dentro da memoria alocada para a matriz, está ben. Pero C non fai comprobación de erros. Se eu fai matriz [1000], é de bo grado só modificar o que pasa - Vai para o inicio da matriz, entón vai posicións 1000 e despois defínese como 0. Non fai ningunha verificación de que ah, iso non ten realmente mil cousas nel. 1000 é moito máis do que eu debería estar cambiando, mentres Java ou algo que vai ter matriz de índice límites ou índice de excepción límites. É por iso que unha morea de linguaxes de alto nivel ten esas cousas onde se ir alén dos límites da matriz, non para que non pode cambiar as cousas de baixo de ti e despois as cousas van moito peor do que só comezando unha excepción dicindo que foi alén do fin da matriz. [Alumno] E así teriamos só cambiou o <= só > [Bowden] Yeah. Debe ser > [Alumno] Dereito. Máis preguntas? Okay. [Estudante] Eu teño unha pregunta. Si >>. [Alumno] Cal é a variable matriz real? [Bowden] Como o que é matriz? Array en si é un símbolo. É só o enderezo do inicio dos 20 bytes que fan referencia. Podes pensar niso como un punteiro, pero é un punteiro constante. Así que as cousas son compilados, a matriz variable non existe máis. [Alumno] Entón como é que atopar o tamaño da matriz? Tamaño da matriz refírese ao tamaño do bloque, que se refire a ese símbolo. Cando fago algo así como printf ("% p \ n", array); imos executalo. O que eu acaba de malo? 'Matriz' matriz declarada aquí. Ah, aquí enriba. Clang é intelixente, e iso pasa a entender que eu declarei a matriz como 5 elementos pero eu estou a indexación na posición 1000. Pode facelo porque estes son só constantes. E só pode ir tan lonxe en conta que eu estou indo alén dos límites da matriz. Pero aviso antes, cando tivemos i ser incorrecta, ela non pode determinar cantos valores eu podería asumir, por iso non pode determinar que eu estaba indo para alén do fin da matriz. Isto é só Clang ser intelixente. Pero agora facer buggy4. Entón o que máis estou facendo de malo? Implicitamente declarando función de biblioteca 'printf ". Vou querer # include. Okay. Agora correndo buggy4. Imprimir o valor da matriz, como eu fixen aquí, imprimir lo como un punteiro impresións algo que se parece iso - bfb8805c - que é un enderezo que é na rexión pila-ISH. Matriz en si é como un punteiro, pero non é un indicador real, Unha vez que un punteiro normal podemos cambiar. Matriz é só unha constante. Os 20 bloques de memoria comezan en 0xbfb8805c enderezo. Entón bfb8805c través deste enderezo +20- ou eu creo -20 - é toda a memoria alocada para esa matriz. Matriz, a variable en si non é almacenada en calquera lugar. Cando está compilando, o compilador - aceno de man para el - pero o compilador só vai usar sabe onde arranxo a ser. El sabe onde comeza esta matriz, e así pode sempre facer as cousas en termos de compensacións que comezo. Ela non precisa dunha variable en si para representar matriz. Pero cando fago algo así como int * p = array; agora p é un punteiro que apunta a esta matriz, e agora p realmente existe na pila. Eu son libre para cambiar p. Eu podo facer p = malloc. Por iso, inicialmente vinculado matriz, agora apunta a un espazo na pila. Eu non podo facer malloc = array. Se Clang é intelixente, vai berrar comigo pronto de cara. En realidade, eu estou seguro que gcc faría iso tamén. Así, o tipo de matriz 'int [5]' non é transferible. Vostede non pode asignar algo a un tipo de matriz porque matriz é só unha constante. É un símbolo que fai referencia a eses 20 bytes. Eu non podo cambiar isto. [Alumno] E onde está o tamaño da matriz almacenada? [Bowden] Non é almacenada en calquera lugar. É cando está compilando. Entón, onde está o tamaño da matriz almacenada? Só se pode usar sizeof (matriz) dentro da función que a matriz é declarada en si. Entón, se eu fai algunha función, foo, e fago (int array []) printf ("% d \ n", sizeof (array)); e aquí eu chamo de foo (matriz); dentro desta función - imos executalo. Este é Clang ser intelixente de novo. Está me dicindo que sizeof no parámetro función de matriz volverá tamaño de 'int *'. Iso sería un erro, se non é o que eu quería que acontecese. Imos realmente desactivar Werror. Aviso. Avisos están ben. É aínda compilará sempre que ten un aviso. . / A.out vai imprimir 4. O aviso que foi xerado é un indicador claro de que deu errado. Esta matriz int só vai imprimir sizeof (int *). Mesmo se eu poñer array [5] aquí, que aínda está indo só para imprimir sizeof (int *). Así, logo que pasar a unha función, a distinción entre matrices e punteiros é inexistente. Isto acontece por ser unha matriz que foi declarado na pila, pero así que pasar ese valor, que 0xbf bla, bla, bla para esta función, entón este punteiro apunta a que a matriz na pila. Entón iso significa que sizeof só se aplica en función de que a matriz foi declarada, o que significa que cando está compilando esta función, Clang cando pasa por esta función, el ve matriz é un array de int tamaño 5. Entón ve sizeof (array). Ben, iso é 20. Isto é realmente como sizeof funciona basicamente para case todos os casos. Sizeof non é unha función, é un operador. Non chama a función sizeof. Sizeof (int), o compilador só vai traducir isto para 4. Entendeu? Okay. [Alumno] Entón, cal é a diferenza entre sizeof (array) no inicio e en foo? Isto é porque nós estamos dicindo sizeof (array), que é do tipo int *, mentres que a matriz aquí abaixo non é do tipo int *, é unha matriz int. [Alumno] Entón se tivese o parámetro na matriz [] no canto de matriz int *, que iso significa que aínda pode cambiar matriz porque agora é un punteiro? [Bowden] Gustoulle? >> [Estudante] Yeah. Pode cambiar matriz dentro da función agora? [Bowden] Vostede podería cambiar matriz en ambos os casos. En ambos os casos, vostede é libre para dicir array [4] = 0. [Alumno] Pero pode facer punto de matriz para algo máis? [Bowden] Oh Si En ambos os casos - >> [estudante] Yeah. [Bowden] A distinción entre array [] e unha matriz int *, non hai ningunha. Tamén pode obter algúns matriz multidimensional aquí por algunha sintaxe cómodo, pero aínda é só un punteiro. Isto significa que eu son libre para facer o array = malloc (sizeof (int)), e agora apuntan a outro lugar. Pero, así como a forma en que funciona sempre e sempre, cambiar esa matriz, tornándose a apuntar cara algo máis non cambiar esa matriz aquí porque é unha copia do argumento, non é un punteiro para ese argumento. E, en realidade, só como unha indicación de que é exactamente o mesmo - xa vimos o que imprime matriz de impresión - o que se imprime o enderezo da matriz ou o enderezo do enderezo da matriz a un deses? Imos ignorar este. Okay. Isto é bo. É agora en execución. A.out /. Matriz de impresión, despois de imprimir o enderezo da matriz, son a mesma cousa. Matriz simplemente non existe. Sabe cando está imprimindo matriz, está imprimindo o símbolo que se refire a eses 20 bytes. Imprimir o enderezo da matriz, así, matriz non existe. El non ten un enderezo, por iso só amosa o enderezo deses 20 bytes. Así que compilar para abaixo, como no seu buggy4 compilado. / A.out, matriz é inexistente. Punteiros existe. Matrices non. Os bloques de memoria que representan a matriz aínda existen, pero a matriz variable e as variables deste tipo non existen. Aqueles son como as principais diferenzas entre matrices e punteiros son así que fan chamadas de funcións, non hai diferenza. Pero dentro da función que a propia matriz é declarada, sizeof funciona de forma diferente sempre que a impresión é o tamaño dos bloques, en vez de o tamaño do tipo, e non pode cambiar iso, porque é un símbolo. Impresión da cousa eo enderezo da cousa imprime a mesma cousa. E iso é moi bonito iso. [Alumno] Podería dicirse que unha vez máis? Eu podería perder algo. Matriz de impresión e enderezo da matriz imprime o mesmo, mentres que se imprimir un punteiro en relación ao enderezo do punteiro, a única cousa que imprime o enderezo do que está apuntando, o outro imprime o enderezo do punteiro para a pila. Pode cambiar un punteiro, non pode cambiar un símbolo da matriz. E punteiro sizeof vai imprimir o tamaño deste tipo de punteiro. Entón, int * p sizeof (p) vai imprimir 4, pero int array [5] print sizeof (array) vai imprimir 20. [Estudante] Entón int array [5] pode imprimir 20? Si >>. É por iso que dentro buggy4 cando adoitaba ser sizeof (array) este estaba facendo i <20, que non é o que quería. Queremos i <5. >> [Alumno] Okay. [Bowden] E entón, logo que comezar a pasar nas funcións, se fixemos int * p = array; dentro desta función, podemos basicamente usar p e matriz en exactamente do mesmo xeito, agás para o problema e sizeof o problema cambiando. Pero p [0] = 1; é o mesmo que dicir array [0] = 1; E así como dicimos foo (matriz), ou foo (p); dentro da función foo, esta é a mesma chamada dúas veces. Non hai ningunha diferenza entre estas dúas chamadas. Todos boa sobre isto? Okay. Temos 10 minutos. Imos tentar obter a través deste programa Typer Hacker, Nesta web, que saíu o ano pasado ou algo así. É só debería ser como escribe aleatoriamente e imprime - Calquera ficheiro que pasa de ter cargado é o que parece que está escribindo. Parece que algún tipo de código do sistema operativo. Isto é o que queremos aplicar. Ten que ter un binario executable chamado hacker_typer que leva un único argumento, o ficheiro para "tipo de hacker". Executando o executable que limpar a pantalla e despois imprimir un personaxe do arquivo pasado en cada vez que o usuario preme unha tecla. Entón, calquera tecla presionada, debe xogar fora e en vez imprimir un personaxe a partir do arquivo Que é o argumento. Vou moi ben dicir o que as cousas que imos ter saber son. Pero queremos comprobar a biblioteca termios. Eu nunca usei esta biblioteca en toda a miña vida, polo que ten propósitos moi mínimas. Pero iso vai ser a biblioteca que podemos usar para xogar fora o personaxe que bateu cando está escribindo en estándar dentro Entón hacker_typer.c, e imos querer # include. Mirando para a páxina man para termios - estou supondo que é terminal de OS ou algo - Eu non sei como le-lo. Mirando para iso, di para incluír estes dous arquivos, entón imos facelo. Primeiro de todo, queremos ter un único argumento, que é o ficheiro que debe abrir. Entón, o que quero facer? Como podo comprobar a ver que eu teño un único argumento? [Estudante] Se argc é igual a el. >> [Bowden] Yeah. Entón, se (argc = 2!) Printf ("Uso:% s [arquivo para abrir]"). Polo tanto, agora eu executar este sen proporcionar un segundo argumento - Oh, eu teño da nova liña - podes ver que di de uso:. / hacker_typer, e, a continuación, o segundo argumento debe ser o ficheiro que quero abrir. Agora, o que fago? Eu quero ler a partir deste ficheiro. ¿Como ler un arquivo? [Alumno] abri-lo primeiro. Si >>. Entón fopen. O que fopen parece? [Alumno] Filename. >> [Bowden] Marabillosa vai ser argv [1]. [Alumno] E entón o que quere facer con ela, entón a - >> [Bowden] Yeah. Entón, se non se lembra, só podería facerse fopen home, onde vai ser un camiño const char *, onde camiño é o nome do ficheiro, modo de const char *. Se ocorrer de non se lembrar do que forma, entón podes ollar para o modo. Dentro de páxinas de manual, o carácter de barra é o que pode usar para buscar as cousas. Entón eu escriba / para a comprobación xeito. n e N son o que pode usar para percorrer os xogos de busca. Aquí di que os puntos de modo argumento para unha cadea comezando con unha das seguintes secuencias. Entón R, arquivo de texto aberto para lectura. Isto é o que queremos facer. Para ler, e quero gardar iso. A cousa vai ser un ficheiro *. Agora o que quero facer? Déame un segundo. Okay. Agora o que quero facer? [Alumno] Comprobar se é NULL. >> [Bowden] Yeah. Cada vez que abrir un arquivo, asegúrese de que vostede é éxito capaz de abrilo. Agora quero facer isto termios onde quero primeiro ler as miñas opcións actuais e gardar os en algo, entón eu quero cambiar a miña configuración tirar calquera personaxe que eu escriba, e entón eu quero actualizar esas opcións. E despois, ao final do programa, quero cambiar de volta para as miñas opcións orixinais. Así, a estrutura vai ser de termios tipo, e eu vou querer dous deles. O primeiro vai ser o meu current_settings, e entón eles van ser os meus hacker_settings. En primeiro lugar, eu vou querer gardar as miñas opcións actuais, entón eu vou querer actualizar hacker_settings, e forma, a finais do meu programa, quero voltar a configuración actual. Entón gardar as configuracións actuais, de xeito que funciona, nós termios home. Vemos que temos ese tcsetattr int, int tcgetattr. Eu pasar un struct termios polo seu punteiro. A forma como iso vai mirar é - eu xa esqueceu o que a función foi chamada. Copia e pega-o. Entón tcgetattr, entón eu quero pasar na estrutura que eu estou salvando a información, que vai ser current_settings, eo primeiro argumento é o descriptor de ficheiro para a cousa que quero gardar os atributos de. O que o descriptor de ficheiro é como calquera vez que abrir un arquivo, el recibe un descritor de ficheiro. Cando fopen argv [1], faise un descritor de ficheiro que está facendo referencia sempre que quere ler ou escribir para el. Ese non é o descriptor de ficheiro que quero usar aquí. Hai tres descritores de ficheiros que ten por defecto, que son estándar, saída estándar e erro estándar. Por defecto, eu creo que é estándar en é 0, fóra estándar é 1, e do erro estándar é 2. Entón, o que quero cambiar a configuración do? Eu quero cambiar as opcións de cada vez que eu bati un personaxe, Eu quero que xogue o personaxe fóra en vez de imprimir lo na pantalla. O fluxo - estándar, saída estándar ou erro estándar - responde ás cousas cando escribir no teclado? >> [Estudante] Standard dentro >> Yeah. Entón eu pode facer 0 ou stdin que eu poida facer. Estou recibindo o current_settings de patrón dentro Agora quero actualizar esas opcións, Entón, primeiro eu vou copiar hacker_settings que os meus current_settings son. E como o traballo de estruturas que vai só copia. Isto copia todos os campos, como sería de esperar. Agora quero actualizar algúns dos campos. Mirando termios, ten que ler unha morea de presente só para ver o que quere buscar, pero as bandeiras que vai querer ollar para son eco, ECHO para caracteres de entrada de eco. Primeiro quero definir - eu xa esqueceu o que os campos son. Isto é o que a estrutura parece. Así, os modos de entrada penso que queremos cambiar. Nós imos ollar para a solución para asegurarse de que é o que queremos cambiar. Queremos cambiar lflag, a fin de evitar a necesidade de ollar a través de todos estes. Queremos cambiar os modos locais. Vostede tería que ler esa cousa toda para entender onde todo pertence que queremos cambiar. Pero é dentro modos locais onde estamos indo querer cambiar isto. Entón hacker_settings.cc_lmode é o que se chama. c_lflag. Isto é onde nós entramos operadores bit a bit. Somos o tipo de fóra do tempo, pero imos pasar por iso rápido. Isto é onde nós entramos operadores bit a bit, onde eu creo que eu dixen un tempo atrás que cando comeza a tratar con bandeiras, vai estar usando operador bitwise moito. Cada bit na bandeira representa algún tipo de comportamento. Entón, aquí, esta bandeira ten unha morea de cousas distintas, onde todos eles significan algo diferente. Pero o que quero facer é simplemente apagar o pouco que corresponde ao ECHO. Así, para desactivar iso fago & = ¬ ECHO. En realidade, eu creo que é como Techo ou algo así. Eu só vou comprobar de novo. I pode termios lo. É só ECHO. ECHO vai ser un único bit. ¬ ECHO vai significar todos os bits son definidos a 1, o que significa que todas as bandeiras son definidas como true excepto para o pouco ECHO. Ao acabar coas miñas bandeiras locais, con iso, significa que todas as bandeiras que están actualmente establecidos para True aínda vai ser definido como verdadeiro. A miña bandeira ECHO é definido como verdadeiro, entón este é necesariamente definida como false na bandeira ECHO. Entón esta liña de código só desactiva a bandeira ECHO. As outras liñas de código, vou copialos no interese de tempo e despois explica-las. Na solución, dixo 0. Probablemente é mellor dicir explicitamente stdin. Repare que eu tamén estou facendo ECHO | ICANON aquí. ICANON se refire a algo separado, o que significa para canónico. O que significa para canónico é xeralmente cando está escribindo a liña de comandos, estándar en non demandar nada ata que bata nova liña. Entón, cando GetString, escribe unha morea de cousas, entón acadou nova liña. É cando el é enviado a norma dentro Ese é o estándar. Cando desactivar o modo canónico, agora todos os personaxes só premer é o que é procesado, que é xeralmente un pouco mal, porque é lento para procesar esas cousas, é por iso que é bo para o buffer-lo en liñas enteiras. Pero eu quero que cada personaxe a ser procesado dende que eu non quero iso para me esperar a bater de nova liña antes de procesar todos os personaxes que eu estiven escribindo. Isto transforma o modo canónico. Este material significa só cando realmente procesa caracteres. Isto significa proceso-los inmediatamente; así que eu estou escribindo eles, proceso-los. E esta é a función que está a actualizar as miñas opcións para estándar en, e medios TCSA facelo agora. As outras opcións son esperar ata todo o que está actualmente no fluxo é procesado. Isto realmente non importa. Só agora cambiar as opcións para ser o que está actualmente en hacker_typer_settings. Eu creo que o chamou hacker_settings, entón imos cambiar isto. Cambiar todo para hacker_settings. Agora a finais do noso programa imos querer desfacer para o que existe actualmente no interior normal_settings, que vai só ollar como e normal_settings. Repare que eu non mudei ningún dos meus normal_settings xa que orixinalmente conseguir. A continuación, só para muda-los de volta, eu paso-los de volta no final. Esta foi a actualización. Okay. Agora aquí dentro eu vou explicar o código no interese de tempo. Non é que moito código. Vemos, lemos un personaxe a partir do ficheiro. Nós o chamamos f. Agora podes fgetc home, pero como fgetc vai funcionar é só que vai voltar o carácter que acaba de ler ou EOF, que corresponde ao final do ficheiro de algún acontecemento ou de erro. Estamos looping, continuando a ler un único carácter do ficheiro, ata que funcionamos fóra de caracteres para ler. E mentres nós estamos facendo isto, esperamos nun único carácter de estándar dentro Cada vez que escriba algo na liña de comandos, que está lendo un personaxe de nivel dentro Entón putchar só vai poñer o char lemos aquí enriba do ficheiro para a saída estándar. Pode putchar home, pero é só poñer na saída estándar, é a impresión que o personaxe. Tamén pode só facer printf ("% c", c); mesma idea. Isto vai facer a maior parte do noso traballo. A última cousa que vai querer facer é só fclose noso arquivo. Se non fclose, que é un baleirado de memoria. Queremos fclose o arquivo que orixinalmente aberto, e eu creo que é iso. Se facemos iso, eu xa teño problemas. Imos ver. O que foi reclamar? Esperado 'int' pero o argumento é do tipo 'struct _IO_FILE *'. Imos ver se isto funciona. Só permitido C99. Augh. Ok, facer hacker_typer. Agora temos descricións máis útiles. Polo tanto, use de identificador non declarado "normal_settings '. Eu non chamalo normal_settings. Chameino current_settings. Entón, imos cambiar todo isto. Agora pasando argumento. Vou facelo 0 por agora. Okay. . / Hacker_typer cp.c. Eu tamén non limpar a pantalla ao comezo. Pero pode ollar cara atrás para o conxunto de problemas última para ver como limpar a pantalla. É só imprimir algúns caracteres Mentres tanto está facendo o que quero facer. Okay. E pensar sobre por que isto precisaba ser 0 en vez de stdin, que debe ser # define 0, este se queixa de que - Antes, cando dixo que non hai descritores de ficheiros, pero entón tamén ten o seu FILE *, un descritor de ficheiro é só un único número enteiro, mentres que un FILE * ten unha morea de cousas asociado a el. A razón que necesitamos dicir 0 en vez de stdin é que stdin é un FILE * que apunta a algo que é referencia descriptor de ficheiro 0. Así, mesmo aquí, cando fago fopen (argv [1], eu estou quedando un * ficheiro de volta. Pero en algún lugar que * FILE é unha cousa correspondente ao descritor de ficheiro para o ficheiro. Se ollar para a páxina do manual para abrir, entón eu creo que vai ter que facer o home aberto 3 - nope - home aberto 2 - Si. Se ollar para a páxina para abrir aberto é como un fopen de nivel inferior, e está retornando ao descritor de ficheiro real. fopen fai unha morea de cousas enriba aberto, que en vez de retornar só descriptor de ficheiro que retorna un ficheiro punteiro * dentro do que é o noso descriptor de ficheiro pequeno. Entón estándar en canto á cousa * file, Considerando 0 refírese só un estándar descriptor do ficheiro en si. Preguntas? [Risas] Blew por iso. Todo ben. Estamos a facer. [Risas] [CS50.TV]