[Powered by Google Translate] [Sección 5 - Más Cómodo]
[Rob Bowden - Harvard University]
[Esta es CS50. - CS50.TV]
>> Como dije en mi e-mail, hay un montón de cosas que usted puede utilizar
que no sea el aparato para hacer realidad los boletines de problemas.
Te recomendamos que lo hagas en el aparato sólo porque entonces nos será más fácil ayudar
y sabemos que todo va a funcionar.
Pero como un ejemplo de que se pueden hacer cosas si, por ejemplo, no tienen acceso
a un aparato o desea trabajar en el sótano del Centro de Ciencias -
que en realidad tienen el aparato demasiado -
si quieres trabajar en cualquier lugar.
Un ejemplo se han visto / oído de SSH?
SSH es básicamente igual que conectarse a algo.
De hecho, ahora mismo estoy SSHed en el aparato.
Nunca trabajar directamente en el aparato.
Aquí está el aparato, y si miras aquí abajo se ve esta dirección IP.
Nunca trabajo en el propio aparato;
Siempre vengo a un iTerm2 ventana / ventana de terminal.
Usted puede SSH a esa dirección IP, ssh jharvard@192.168.129.128.
Me acuerdo de ese número con mucha facilidad debido a que es un bonito patrón.
Pero eso me va a pedir mi contraseña, y ahora estoy en el aparato.
Básicamente, en este punto, si se abre un terminal en el interior del propio aparato,
esta interfaz, sin embargo usted lo usa, es exactamente el mismo
como la interfaz que estoy usando aquí, pero ahora estamos SSHed.
Usted no tiene que SSH al dispositivo.
Un ejemplo de otro lugar que podría SSH es que estoy bastante seguro de que tiene por defecto -
Oh. Más grande.
Todos ustedes deben tener en cuenta FAS por defecto en los servidores de FAS.
Para mí, lo haría SSH a rbowden@nice.fas.harvard.edu.
Va a pedirle que la primera vez, y usted dice que sí.
Mi contraseña es sólo va a ser mi contraseña FAS.
Y ahora, estoy SSHed a los servidores buenos, y puedo hacer lo que quiera aquí.
Una gran cantidad de clases que se podrían adoptar, como 124, van a tener que subir cosas a aquí
que presentar efectivamente sus boletines de problemas.
Pero dice que no tiene acceso a su aparato.
A continuación, puede hacer cosas, como aquí se dirá -
Esto es sólo nuestra sección de preguntas.
Se le pide que lo hagamos en el aparato.
En lugar de eso sólo lo hará en el servidor.
Me voy a descomprimir eso.
El problema va a ser que usted está acostumbrado a usar algo como gedit
o lo que sea en el interior del aparato.
No vas a tener que en el servidor de FAS.
Todo esto es sólo va a ser esta interfaz textual.
Así que usted podría cualquiera de ellos, tratar de aprender un editor de texto que se tiene.
Tienen Nano.
Nano es bastante fácil de usar.
Usted puede utilizar sus flechas y escribe normalmente.
Así que no es difícil.
Si usted quiere conseguir realmente de lujo puede usar Emacs,
que yo probablemente no debería haber abierto porque no sé ni cómo cerrar Emacs.
X Control, Control C? Si.
O puede usar Vim, que es lo que yo uso.
Y esas son sus opciones.
Si no quieres hacer eso, usted también puede, si nos fijamos en manual.cs50.net--
Oh.
En un PC, puede usar SSH PuTTY,
que vas a tener que descargar por separado.
En un Mac, puede simplemente usar predeterminado Terminal o puede descargar iTerm2,
que es como un terminal bonito, lujoso.
Si usted va a manual.cs50.net verás un enlace a Notepad + +,
que es lo que se puede utilizar en un PC.
Te permite SFTP de Notepad + +, que es básicamente SSH.
Lo que esto le permitirá hacer es editar tus archivos de forma local,
y luego cada vez que quieras salvar, salvará a nice.fas,
donde usted puede entonces correr.
Y el equivalente en Mac va a ser TextWrangler.
Por lo tanto, le permite hacer lo mismo.
Te permite editar archivos de forma local y guardarlos en nice.fas,
donde usted puede entonces correr.
Así que si alguna vez atrapado sin un aparato, tiene las siguientes opciones
que todavía hace sus boletines de problemas.
El único problema va a ser que no vamos a tener la biblioteca CS50
porque nice.fas por defecto no tienen eso.
Puede descargar la biblioteca CS50 -
Yo no creo que sea necesario que en este punto.
Puede descargar la biblioteca CS50 y copiarlo a nice.fas,
o creo que en este momento no usarlo nunca más de todos modos.
O si lo hacemos, puede por el momento, sustituirlo por
las implementaciones de las funciones en la biblioteca CS50 de todos modos.
Así que no debería ser parte de una restricción.
Y eso es todo.
>> Voy a volver a la aplicación ahora, vamos a hacer todo lo posible en el aparato.
En cuanto a nuestra sección de preguntas, al principio, como dije en mi e-mail,
tenemos que hablar de la corta se suponía que ver.
Tenemos la reorientación y Tubos y estas tres preguntas.
>> ¿A qué corriente no funciona como printf escribir de forma predeterminada?
Así corriente. ¿Qué es un río?
Una corriente es básicamente como si fuera sólo un poco -
Ni siquiera es una fuente de 1s y 0s.
La corriente está pidiendo aquí es la salida estándar.
Y fuera tan estándar es una corriente que cuando se escribe en él,
que aparece en la pantalla.
Fuera Standard, por flujo, esto significa que usted acaba de escribir 1s y 0s a la misma,
y el otro extremo de salida estándar sólo lee de esa corriente.
Es sólo una cadena de 1s y 0s.
Usted puede escribir a los arroyos o puede leer de los arroyos
dependiendo de lo que la corriente que realmente es.
Las otras dos corrientes de defecto son estándar y de error estándar.
Es estándar en cada vez que se GetString, está esperando para que usted pueda materia de entrada.
Por lo que te espera, en realidad espera en la norma en,
que en realidad es lo que pasa cuando se escribe en el teclado.
Estás escribiendo en estándar pulg
Error estándar es básicamente equivalente a la salida estándar,
pero es especializada en la que al imprimir en error estándar,
se supone que debes imprimir sólo mensajes de error para que
para que pueda diferenciar entre los mensajes impresos regulares a la pantalla
frente a los mensajes de error dependiendo de si fueron a la salida estándar o error estándar.
Los archivos también.
Fuera estándar, estándar en, y el error estándar son corrientes sólo especiales,
pero en realidad cualquier archivo, cuando se abre un archivo, éste se convierte en un flujo de bytes
donde sólo se puede leer de esa corriente.
Usted, en su mayor parte, sólo puedo pensar en un archivo como una secuencia de bytes.
Entonces, ¿qué es lo que las corrientes escribir de forma predeterminada? Por Norma.
>> ¿Cuál es la diferencia entre> y >>?
¿Alguien ver el vídeo de antemano? Bien.
> Va a ser cómo redirigir a los archivos,
y >> también va a redirigir la salida en archivos,
pero está vez va a anexar al expediente.
Por ejemplo, los vamos a decir resulta que tengo dict aquí,
y las cosas sólo en el interior de dict es gato, gato, perro, pez, perro.
Un comando que usted tiene en la línea de comandos es gato,
que sólo va a imprimir lo que hay en un archivo.
Así que cuando digo dict gato, que va a imprimir gato, gato, perro, pez, perro. Eso es todo gato hace.
Eso significa que se imprime en la salida estándar gato, gato, perro, pez, perro.
Si en lugar que desee redirigir a un archivo, puede utilizar> y redireccionarlo a lo que el archivo es.
Voy a llamar al archivo archivo.
Así que ahora si ls, voy a ver que tengo un nuevo archivo llamado archivo.
Y si lo abre, va a tener exactamente lo gatos, acondicionados en la línea de comandos.
Así que ahora si lo hago de nuevo, entonces va a redirigir la salida en un archivo,
y yo voy a tener exactamente la misma cosa.
Así que, técnicamente, se anuló por completo lo que teníamos.
Y vamos a ver si puedo cambiar dict, saqué perro.
Ahora bien, si dict gato en el archivo de nuevo, vamos a tener la nueva versión con el perro eliminado.
Por lo tanto, se anula completamente.
En cambio, si usamos >>, que va a adjuntar el archivo.
Ahora, al abrir el archivo, vemos que tenemos la misma cosa dos veces impreso
porque era allí una vez, entonces adjunta a la original.
Así que eso es lo que> y >> hacer.
¿La siguiente pregunta - No pregunte al respecto.
>> El otro que tenemos es <, que si> redirecciona la salida estándar,
<Se va a redirigir estándar pulg
Vamos a ver si tenemos un ejemplo.
Soy capaz de escribir un muy rápido.
Tomemos cualquier archivo, hello.c.
Archivo relativamente sencillo.
Me estoy haciendo una cadena y luego imprimir "Hola" cualquiera que sea la cadena que acaba de entrar era.
Así que hola y luego. / Hello.
Ahora se me llevó a escribir algo,
lo que significa que está esperando en las cosas que se introducirán en estándar pulg
Así que lo que yo quiera entrar en estándar pulg Sólo vamos a decir Hola, Rob!
Entonces está imprimiendo a la salida estándar Hola, Rob!
Si lo hago. / Hola y luego redireccionar,
por ahora sólo se puede redirigir a un archivo.
Así que si me pongo en algún archivo, txt, y puse Rob,
hola si me quedo y luego redirigir el archivo txt a. / hola, que va a decir Hola, Rob! inmediatamente.
Cuando por primera vez llega a GetString y está esperando en la norma en,
estándar ya no se espera en el teclado para los datos que se ingresan.
En cambio, hemos redirigido estándar para leer el archivo txt.
Y por lo que va a leer desde el archivo txt, que es sólo la línea de Rob,
y entonces va a imprimir Hola, Rob!
Y si yo quería, yo también podía hacer. / Hello <txt
y entonces el estándar que el mismo de la impresión, que es Hola, Rob!,
Puedo redireccionar que en su propio archivo.
Voy a llamar al archivo hola - no, no lo haré, porque ese es el ejecutable - txt2.
Ahora, txt2 va a tener la salida de. / Hello <txt, que va a ser ¡Hola, Rob!
>> ¿Preguntas?
>> Bien. Así que aquí tenemos tubería.
Los tubos son la última unidad de redirección.
>> Oh. Supongo que una unidad más de redirección es si en vez de> hacer 2>,
que está redirigiendo error estándar.
Así que si algo salía de error estándar, no se pone en txt2.
Pero note si lo hago 2>, entonces es todavía la impresión Hola, Rob! a la línea de comandos
porque estoy solo redirigir el error estándar, no estoy redirigiendo la salida estándar.
Error estándar y la salida estándar son diferentes.
Si querías escribir realmente el error estándar
entonces yo podría cambiar esto sea fprintf a stderr.
Así printf, por defecto, se imprime en la salida estándar.
Si desea imprimir en el error estándar de forma manual, entonces tengo que usar fprintf
y especificar lo que desea imprimir.
Si en cambio lo hice stdout fprintf, entonces eso es básicamente equivalente a printf.
Pero fprintf al error estándar.
Así que ahora, si puedo redirigir esto en txt2, Hola, Rob! todavía se está impreso en la línea de comandos
ya que se está haciendo visualiza como error estándar y sólo estoy redirigiendo la salida estándar.
Si ahora redirigir el error estándar, ya que no se imprimen y txt2 va a ser ¡Hola, Rob!
Así que ahora, usted puede imprimir sus errores reales en el error estándar
e imprimir sus mensajes regulares a la salida estándar.
Y así, cuando se ejecuta el programa, se puede ejecutar como. / Hola este tipo con el 2>
para que el programa va a funcionar con normalidad,
pero cualquier mensaje de error que usted recibe puede comprobar más adelante en su registro de errores,
lo que los errores, y luego buscar el archivo más tarde y tendrá errores los errores que ocurrieron.
>> ¿Preguntas?
>> El último es pipa, que se puede pensar en como tomar la salida estándar de un comando
y lo que es el estándar en el siguiente comando.
Un ejemplo de ello es eco es algo línea de comandos
que sólo va a hacerse eco de lo que me pone como argumento. No voy a poner comillas.
Echo bla, bla, bla, sólo va a imprimir, bla, bla, bla.
Antes, cuando me dijo que tenía que poner Rob en un archivo txt
porque sólo puedo redirigir los archivos txt, en cambio, / si me hago eco de Rob
y luego canalizarla en. / hola, que también hará el mismo tipo de cosas.
Esto es tomar la salida de este comando, el eco Rob,
y que sirva como insumo para el. / hello.
Usted puede pensar en él como el primer eco Rob redirigir a un archivo
y luego ingrese en. / hola ese archivo que se emiten justo.
Pero se necesita el archivo temporal de la imagen.
>> Las preguntas sobre eso?
>> La pregunta que viene va a implicar esto.
¿Qué gasoducto podría usar para encontrar el número de nombres únicos en un archivo llamado nombres.txt?
Los comandos que vamos a querer utilizar aquí son únicos, por lo uniq, a continuación, wc.
Usted puede hacer uniq hombre a mirar realmente lo que hace,
y que sólo va a filtrar las líneas que coincidan adyacentes de la entrada.
Y el hombre wc va a imprimir la palabra nueva línea, y el recuento de bytes para cada archivo.
Y el último que vamos a querer usar es una especie,
que se va a ordenar sólo las líneas de archivo txt.
Si hago algún archivo txt, nombres.txt, y es Rob, Tommy, Joseph, Tommy, Joseph, RJ, Rob,
lo que quiero hacer aquí es encontrar el número de nombres únicos en este archivo.
Entonces, ¿qué debería ser la respuesta? >> [Estudiante] 4. Sí >>.
Debería ser 4, ya que Rob, Tommy, Joseph, RJ son los únicos nombres únicos en este archivo.
El primer paso, si acabo de hacer recuento de palabras en nombres.txt,
esto es en realidad me está diciendo todo.
Esto es en realidad la impresión - A ver, hombre wc - nuevas líneas, palabras, y el recuento de bytes.
Si sólo se preocupan por las líneas, entonces sólo puede hacer wc-l nombres.txt.
Así que ese es el paso 1.
Pero yo no quiero wc-l nombres.txt nombres.txt porque sólo contiene todos los nombres,
y quiero filtrar los seres no únicos.
Así que si lo hago nombres.txt uniq, que no acaba de darme lo que quiero
porque los nombres duplicados todavía están allí.
¿Por qué es eso? ¿Por qué no uniq hacer lo que quiero?
[Estudiante] Los duplicados no son [inaudible] >> Si.
Recuerde que la página de manual para uniq dice filtro de líneas coincidentes adyacentes.
No son adyacentes, por lo que no los filtra.
Si ordenarlos en primer lugar, nombres.txt tipo va a poner todas las líneas duplicadas entre sí.
Así que ahora nombres.txt especie es eso.
Voy a querer usar eso como la entrada a uniq, que es | uniq.
Eso me da Joseph, RJ, Rob, Tommy,
y quiero usar eso como la entrada a wc-l,
que me va a dar 4.
Como dice aquí, lo gasoducto podría utilizar?
Usted puede hacer un montón de cosas como el uso de una serie de comandos
donde se utiliza la salida de un comando como entrada para el comando siguiente.
Usted puede hacer un montón de cosas, un montón de cosas inteligentes.
>> ¿Preguntas?
Bien.
Eso es para las tuberías y la redirección.
>> Ahora vamos a las cosas reales, las cosas codificación.
Dentro de este PDF, podrás ver este comando,
y usted desea ejecutar este comando en el aparato.
wget es el comando para sólo conseguir algo de Internet, en el fondo,
así wget URL y esto.
Si usted fue a la siguiente dirección URL en el navegador, se descarga el archivo.
Me acaba de hacer clic en él, por lo que el archivo descargado para mí.
Pero escribir wget de esa cosa dentro de la terminal
sólo va a descargar en su terminal.
Tengo section5.zip, y usted desea descomprimir section5.zip,
que te va a dar una carpeta llamada Sección 5,
que va a tener todos los archivos que vamos a utilizar hoy en día dentro de ella.
Como los nombres de estos programas sugieren archivos, son un poco buggy,
por lo que su misión es averiguar por qué usar gdb.
¿Todo el mundo los ha descargado / saber cómo los ha descargado
en su electrodoméstico? Bien.
>> Ejecución de ./buggy1, dirá Segmentation fault (core dumped),
que cada vez que recibe un error de segmentación, es una mala cosa.
¿Bajo qué circunstancias se puede conseguir una violación de segmento?
[Estudiante] desreferenciación un puntero nulo. Sí >>. Así que es un ejemplo.
Desreferenciación un puntero nulo vas a conseguir una violación de segmento.
Lo que significa es una violación de segmento que está tocando de memoria que no se debe tocar.
Así desreferencia un puntero nulo está tocando la dirección 0,
y, básicamente, todos los ordenadores hoy en día dicen que el 0 es la dirección de memoria que no se debe tocar.
Así que por eso se produce una eliminación de referencias a punteros nulos en una violación de segmento.
Cuando te quedas a no inicializar un puntero, entonces tiene un valor basura,
y así, cuando se intenta eliminar la referencia que, con toda probabilidad, usted está tocando memoria
que está en el medio de la nada.
Si le sucede a tener suerte y el valor de la basura
paso para que apunte a algún lugar en la pila o algo,
luego cuando desreferencia puntero que los que no ha inicializado,
nada va a salir mal.
Pero si se está señalando, por ejemplo, en algún lugar entre la pila y el heap,
o es simplemente apuntando a algún lugar que no ha sido utilizado por el programa, sin embargo,
entonces usted está tocando de memoria que no se debe tocar y segfault ti.
Al escribir una función recursiva y recursivamente demasiadas veces
y su pila es demasiado grande y choca con las cosas de la pila
que no debe chocar con, estás tocando de memoria que no se debe tocar,
por lo que violación de segmento.
Eso es lo que es una violación de segmento.
>> También es la misma razón que si usted tiene una cadena como -
vamos a volver al programa anterior.
En hello.c--soy sólo va a hacer otra cosa.
char * s = "hola mundo";
Si utilizo * s = algo o s [0] = 'X';
así que hola. / hola, ¿por qué esa violación de segmento?
¿Por qué esta violación de segmento?
¿Qué espera que suceda?
Si lo hiciera printf ("% s \ n", s), lo que se puede esperar para ser impreso?
[Estudiante] X hello. Sí >>.
El problema es que cuando se declara una cadena como esta,
s es un puntero que va a ir a la pila,
y lo que s está señalando es esta cadena que está contenido en memoria de sólo lectura.
Así que sólo por el nombre, la memoria de sólo lectura, usted debe conseguir la idea
que si intenta cambiar lo que hay en memoria de sólo lectura,
usted está haciendo algo que no debería estar haciendo con la memoria y la violación de segmento ti.
Esto es realmente una gran diferencia entre char * s y char s [].
Así char s [], ahora esta cadena se va a poner en la pila,
y la pila no es de sólo lectura, lo que significa que esto debería funcionar perfectamente bien.
Y lo hace.
Recuerde que cuando hago char * s = "hola mundo", s es en sí mismo en la pila
pero señala sa en otro lugar, y que en otro lugar pasa a ser de sólo lectura.
Pero char s [] es sólo algo en la pila.
Así que ese es otro ejemplo de una violación de segmento sucediendo.
>> Vimos que ./buggy1 dio lugar a una violación de segmento.
En teoría, no debería mirar buggy1.c inmediatamente.
En su lugar, vamos a mirar a través de gdb.
Tenga en cuenta que cuando usted consigue Segmentation fault (core dumped),
recibe este archivo a través de aquí se llama núcleo.
Si ls-l, veremos que el núcleo es normalmente un archivo bastante grande.
Este es el número de bytes del archivo, por lo que parece que es algo de 250 kilobytes.
La razón de esto es que lo que el volcado del núcleo es realmente
Se presenta cuando el programa se bloquea, el estado de la memoria de su programa
sólo se copia y se pega en este archivo.
Esto se vierten en ese archivo.
Este programa, al tiempo que corría, pasó a tener un uso de memoria de alrededor de 250 kilobytes,
y eso es lo que he vertido en este archivo.
Ahora se puede ver en el archivo si lo hacemos gdb núcleo buggy1.
Sólo podemos hacer gdb buggy1, y que sólo se pondrá en marcha gdb con regularidad,
utilizando buggy1 como su archivo de entrada.
Pero si lo hace gdb núcleo buggy1, entonces está específicamente va a poner en marcha gdb
al ver que el archivo principal.
Y diciendo buggy1 gdb medio sabe que ese archivo central proviene del programa buggy1.
Así gdb buggy1 núcleo va a llevarnos inmediatamente
a los que el programa pasó a terminar.
Vemos aquí el Programa terminó con señal 11, fallo de segmentación.
Nos pasó a ver una línea de montaje, lo que probablemente no es muy útil.
Pero si escribe bt o backtrace, eso va a ser la función
que nos da la lista de nuestros marcos de pila actual.
Así backtrace. Parece que sólo tenemos dos marcos de pila.
El primero es nuestro marco de pila principal,
y el segundo es el marco de pila para esta función que nos ha tocado estar,
que parece que sólo tenemos el código ensamblador para.
Así que vamos a volver a nuestra función principal,
y para ello podemos hacer un marco, y creo que también podemos hacer hacia abajo,
pero casi nunca lo hacen hacia abajo - arriba o abajo. Si.
Arriba y abajo. Hasta que nos lleva a un marco de pila, abajo te trae abajo un marco de pila.
Tiendo a no usarla.
Acabo de decir específicamente el cuadro 1, que se vaya al fotograma con la etiqueta 1.
Cuadro 1 nos va a poner en marco de pila principal,
y dice que aquí la línea de código que nos encontremos en.
Si queríamos un par de líneas de código, se puede afirmar lista,
y que nos va a dar todas las líneas de código que lo rodean.
La línea que estaba en segfaulted 6:
if (strcmp ("CS50 rocas", argv [1]) == 0).
Si no está claro, sin embargo, usted puede ir directamente desde aquí sólo pensar por qué segfaulted.
Pero podemos dar un paso más y decir: "¿Por qué argv [1] violación de segmento?"
Vamos a imprimir argv [1], y parece que es 0x0, que es el puntero nulo.
Estamos strcmping CS50 rocas y nulos, por lo que va a violación de segmento.
¿Y por qué es argv [1] null?
[Estudiante] Debido a que no le dio ningún argumento de línea de comandos.
Si. Nosotros no le dio ningún argumento de línea de comandos.
Así ./buggy1 sólo va a tener argv [0] sea ./buggy1.
No va a tener un argv [1], por lo que va a violación de segmento.
Pero si, en cambio, lo hago sólo CS50, va a decir que te dan un D
porque eso es lo que se supone que debe hacer.
En cuanto a buggy1.c, se supone que debe imprimir "Se obtiene una D" -
Si argv [1] no es "CS50 rocas", "Usted obtiene una D", de lo "Se obtiene una A!"
Así que si queremos una A, necesitamos esto para comparar como verdadero,
lo que significa que se compara con 0.
Así que argv [1] tiene que ser "piedras" CS50.
Si quieres hacer eso en la línea de comandos, debe utilizar \ para escapar al espacio.
Así CS50 \ rocas y se obtiene una A!
Si usted no hace la barra invertida, ¿por qué no funciona?
[Estudiante] Se trata de dos argumentos diferentes. Sí >>.
Argv [1] va a ser CS50, y argv [2] va a ser rocas. Bien.
>> Ahora ./buggy2 va a violación de segmento nuevo.
En lugar de abrir el archivo con su núcleo, sólo tendremos que abrir buggy2 directamente,
así gdb buggy2.
Ahora bien, si acaba de ejecutar el programa, entonces va a decir el Programa recibió SIGSEGV,
que es el error de segmentación de la señal, y aquí es donde sucedió a suceder.
En cuanto a nuestro backtrace, vemos que estábamos en el oh_no función,
que fue llamado por el dinky función, que fue llamado por el binky función,
que fue convocada por principal.
También podemos ver los argumentos para estas funciones.
El argumento de mala muerte y binky fue de 1.
Si tenemos lista la función oh_no, vemos que sólo está haciendo oh_no char ** s = NULL;
* S = "BOOM";
¿Por que no?
[Estudiante] No se puede eliminar la referencia al puntero nulo? Sí >>.
Esto es sólo decir s es NULL, independientemente de si que pasa a ser un char **,
que, dependiendo de cómo se interprete, podría ser un puntero a un puntero a una cadena
o una matriz de cadenas.
Es s es NULL, por lo que * s se desreferencia un puntero nulo,
por lo que este se va a estrellar.
Esta es una de las maneras más rápidas le sea posible violación de segmento.
Es simplemente declarar un puntero nulo y segfaulting inmediatamente.
Eso es lo que oh_no está haciendo.
Si subimos un marco, entonces vamos a entrar en la función que llamó oh_no.
Tengo que hacer eso.
Si no se introduce un comando y simplemente pulse Enter de nuevo,
se acaba de repetir el comando anterior que se ejecutó.
Estamos en el cuadro 1.
Añadir este marco, vemos aquí es nuestra función.
Usted puede golpear lista de nuevo, o puedes hacer la lista 20 y una lista de más.
La función dinky dice que si i es 1, entonces vaya a la función oh_no,
sino va a la función furtivo.
Y sé que es 1 porque nos ha tocado ver aquí
dinky que se ha llamado con el argumento 1.
O puede simplemente puedo imprimir y dirá i es 1.
Actualmente estamos en mala muerte, y si nos vamos a otro marco, sabemos que va a terminar en binky.
Arriba. Ahora estamos en binky.
Añadir esta función - la lista de antes del descanso me interrumpió -
lo que comenzó como si i es 0, entonces vamos a llamarlo oh_no, que llame mala muerte.
Sabemos que era 1, por lo que llamó de mala muerte.
Y ahora que estamos de vuelta en main, y principal es sólo va a ser int i = rand ()% 3;
Eso sólo va a darle un número aleatorio que es 0, 1 ó 2.
Se va a llamar binky con ese número, y lo devolverá 0.
En cuanto a esto,
caminando a través del programa de forma manual sin ejecutarlo inmediatamente,
debería establecer un punto de quiebre en la principal, lo que significa que cuando se corre el programa
su programa se ejecuta hasta que se llega a un punto de quiebre.
Entonces, ejecutar el programa, se ejecutará y luego llegará a la función principal y dejar de correr.
Ahora estamos dentro de la principal, y el paso siguiente o nos va a llevar a la siguiente línea de código.
Usted puede realizar el paso o la siguiente.
Golpear siguiente, ahora me ha sido ajustado a rand ()% 3, por lo que podemos imprimir el valor de i,
y lo voy a decir que es 1.
Ahora bien, no importa si usamos o siguiente paso.
Supongo que importaba en la anterior, pero nos gustaría usar a continuación.
Si usamos paso, entramos en la función, lo que significa ver la cosa real
que está sucediendo dentro de Binky.
Si usamos siguiente, entonces significa ir sobre la función
y sólo tiene que ir a la siguiente línea de código en nuestra función principal.
Aquí mismo, en esta línea, yo estaba en donde decía rand ()% 3;
si lo hice paso, entraría en la ejecución de rand
y ver lo que está ocurriendo allí, y podría pasar por la función rand.
Pero no se preocupan por la función rand.
Sólo quiero ir a la siguiente línea de código en el principal, por lo que utilizará a continuación.
Pero ahora sí me importa acerca de la función binky, así que quiero entrar en eso.
Ahora estoy en binky.
La primera línea de código que va a decir if (i == 0), doy un paso,
vemos que terminamos en mala muerte.
Si nosotros, las cosas de la lista, vemos que lo revise i es = 0.
i no es igual a 0, por lo que se fue a la condición más,
que se va a llamar dinky (i).
Es posible que se confunda.
Si sólo se observa en estas líneas directamente, se podría pensar if (i == 0),
bien, entonces di un paso y ahora estoy en dinky (i),
se podría pensar que debe significar i = 0 o algo así.
No. Sólo significa que sabe que puede pegarse directamente a la línea dinky (i).
Porque no es 0, el siguiente paso no se va a acabar en el otro.
Más no es una línea que va a parar en el.
Es sólo va a ir a la siguiente línea en realidad puede ejecutar, que es dinky (i).
Al entrar en dinky (i), vemos if (i == 1).
Lo que sí sé = 1, así que cuando damos un paso, sabemos que vamos a terminar en oh_no
i = 1, porque llama a la función oh_no, que se puede caminar en,
que se va a establecer char ** s = NULL e inmediatamente "BOOM".
Y luego mirando realmente la aplicación de buggy2,
esto, me acaba de obtener un número aleatorio - 0, 1, o 2 - llamado Binky,
que si i es 0 lo llama oh_no, de lo que llama dinky, que llega hasta aquí.
Si i es 1, llamada oh_no, que llame slinky, que viene aquí,
si i es 2, llamar oh_no.
Ni siquiera creo que haya una manera -
¿Alguien ve una manera de hacer de éste un programa que no violación de segmento?
Porque a menos que me falta algo, si i es 0, usted inmediatamente violación de segmento,
otra cosa que ir a una función que si i es usted un error de segmentación,
otra cosa que ir a una función en la que si i es 2 que violación de segmento.
Así que no importa lo que hagas, violación de segmento.
>> Creo que una manera de arreglarlo sería en lugar de hacer char ** s = NULL,
usted podría malloc espacio para esa cadena.
Podríamos hacer malloc (sizeof) - sizeof qué?
[Estudiante] (char) * 5? >> ¿Esto parece correcto?
Asumo que esto va a funcionar si realmente funcionó, pero no es lo que estoy buscando.
Mira el tipo de s. Vamos a añadir * int, int modo * x.
Me gustaría hacer malloc (sizeof (int)).
O si yo quería una matriz de 5, lo haría (sizeof (int) * 5);
¿Qué pasa si tengo un int **?
Lo que yo haría malloc?
[Estudiante] Tamaño del puntero. Sí >>. (Sizeof (int *));
Lo mismo aquí.
Quiero (sizeof (char *));
Esto va a asignar espacio para el puntero que apunta a "BOOM".
No es necesario reservar espacio para "BOOM" en sí
porque esto es básicamente equivalente a lo que dije antes
de char * x = "BOOM".
"BOOM" ya existe. Sucede que existen en la región de sólo lectura de la memoria.
Pero lo que ya existe, lo que significa que esta línea de código, si s es un char **,
entonces s * es un char * y que está configurando este char * para indicar "BOOM".
Si quisiera copiar "BOOM" en s, entonces yo tendría que asignar espacio para el s.
Haré * s = malloc (sizeof (char) * 5);
¿Por qué cinco?
4 ¿Por qué no? Parece que "BOOM" es de 4 caracteres. >> [Estudiante] El carácter nulo.
Si. Todas las cadenas van a necesitar el carácter nulo.
Ahora puedo hacer algo como strcat - ¿Cuál es la función para copiar una cadena?
[Estudiante] cpy? >> Strcpy.
hombre strcpy.
Así strcpy o strncpy.
strncpy es un poco más seguro ya que se puede especificar con exactitud el número de caracteres,
pero en este caso no importa porque lo que sabemos.
Así strcpy y mirar en los argumentos.
El primer argumento es nuestro destino.
El segundo argumento es nuestra fuente.
Vamos a copiar en nuestro destino * s el puntero "BOOM".
¿Por qué querría hacer esto con un strcpy en lugar de lo que teníamos antes
de * s = "BOOM"?
Hay una razón es posible que desee hacer esto, pero ¿qué es eso?
[Estudiante] Si desea cambiar algo en el "BOOM". Sí >>.
Ahora puedo hacer algo como s [0] = 'X';
porque los puntos s al montón y espacio que en el montón que s está apuntando a
es un puntero a más espacio en el montón, que es el almacenamiento de "BOOM".
Así que esta copia de "BOOM" se almacena en el montón.
Hay técnicamente dos copias del "boom" de nuestro programa.
No es el primero que se acaba de dar por esta constante cadena "BOOM",
y la segunda copia del "boom", strcpy creó la copia de "BOOM".
Sin embargo, la copia de "BOOM" se almacena en el montón, y el montón eres libre de cambiar.
La pila no es de sólo lectura, lo que significa que s [0]
va a permitir que usted cambie el valor de "BOOM".
Esto va a permitir cambiar esos caracteres.
>> ¿Preguntas?
Bien.
>> Pasando a buggy3, gdb vamos buggy3.
Acabamos de correr y vemos que obtenemos una violación de segmento.
Si backtrace, sólo hay dos funciones.
Si subimos a nuestra función principal, vemos que segfaulted en esta línea.
Así que sólo mirar a esta línea, for (int fila = 0; fgets esto hace NULL no es igual;
línea + +).
Nuestro cuadro anterior se llamaba _IO_fgets.
Usted verá que una gran cantidad de funciones integradas C,
que cuando llegue la violación de segmento, habrá nombres de las funciones realmente críptico
como éste _IO_fgets.
Pero eso va a relacionar con la presente convocatoria fgets.
En algún lugar dentro de aquí, estamos segfaulting.
Si nos fijamos en los argumentos a fgets, podemos imprimir buffer.
Vamos a imprimir como - Oh, no.
Imprimir no va a funcionar exactamente como yo quiero que haga.
Vamos a ver el programa actual.
Buffer es un array de caracteres. Se trata de una matriz de caracteres de 128 caracteres.
Así que cuando digo búfer de impresión, va a imprimir esos 128 caracteres,
que supongo que es lo que se espera.
Lo que se busca es imprimir la dirección de buffer,
pero que en realidad no me dice mucho.
Así que cuando se me ocurre decir aquí amortiguador x, que me muestra 0xbffff090,
que, si te acuerdas de antes o punto alguno, Oxbffff tiende a ser una región de pila-ish.
La pila tiende a comenzar en algún lugar un poco menos de 0xc000.
Sólo por ver esta dirección, ya sé que el buffer está ocurriendo en la pila.
Reinicio de mi programa, ejecutar, arriba, amortiguar lo que vimos fue esta secuencia de caracteres
que son más o menos sentido.
Después de imprimir el archivo, ¿qué archivo parece?
[Estudiante] Null. Sí >>.
El archivo es un de tipo * FILE, por lo que es un puntero,
y el valor de dicho puntero es nulo.
Así fgets se va a tratar de leer ese puntero en forma indirecta,
pero con el fin de acceder a ese puntero, que tiene que dejar de hacer referencia.
O bien, con el fin de acceder a lo que debe apuntar a, It desreferencia.
Así que es eliminar la referencia a un puntero nulo y segfaults ella.
Podría haberlo reiniciado allí.
Si rompemos a nuestro punto principal y correr,
la primera línea de código es char * filename = "nonexistent.txt";
Esto debería dar una pista bastante grande de por qué este programa falla.
Escribiendo siguiente me lleva a la siguiente línea, en el que abrir este archivo,
y entonces inmediatamente entrar en nuestra línea, donde una vez me golpeó próximo, va a violación de segmento.
¿Alguien quiere echar una razón por la que podría ser segfaulting?
[Estudiante] El archivo no existe. Sí >>.
Esto se supone que es un indicio
que cada vez que va a abrir un archivo que usted necesita para comprobar que el archivo existe en realidad.
Así que aquí, "nonexistent.txt";
Al nombre de archivo fopen para la lectura, entonces necesito decir
if (archivo == NULL) y decir printf ("El archivo no existe!"
o - mejor aún - filename); return 1;
Así que ahora comprobamos si es NULL
antes de realmente continuar y tratar de leer ese archivo.
Podemos rehacer sólo para ver que funciona.
Tenía la intención de incluir una nueva línea.
Así que ahora nonexistent.txt no existe.
Siempre se debe comprobar si este tipo de cosas.
Siempre se debe comprobar para ver si fopen devuelve NULL.
Siempre se debe comprobar para asegurarse de que malloc no devuelve NULL,
o de lo contrario violación de segmento.
>> Ahora buggy4.c.
En ejecución. Supongo que esto está a la espera de la entrada o posiblemente bucle infinito.
Sí, es un bucle infinito.
Así buggy4. Parece que estamos en bucle infinito.
Podemos dividir a principal, ejecute el programa.
En gdb, siempre y cuando la abreviatura que se utiliza es ambigua
o abreviaturas especiales que ofrecen para usted,
entonces usted puede utilizar n para usar a continuación en lugar de tener que escribir al lado todo el camino.
Y ahora que me he golpeado n una vez, sólo puede pulsar Enter para continuar al lado
en lugar de tener que pulsar Enter n, n Intro, n Intro.
Parece como si estuviera en una especie de bucle para que se ajuste array [i] a 0.
Parece que nunca estoy saliendo de este bucle for.
Si yo puedo imprimir, así que es 2, entonces voy a ir después.
Voy a imprimir i, i es 3, entonces voy a ir después.
Voy a imprimir i e i es 3. A continuación, imprima i, i es 4.
En realidad, la impresión sizeof (array), lo que el tamaño de la matriz es de 20.
Pero parece que hay algún comando gdb especial para ir hasta que algo sucede.
Es como fijar una condición sobre el valor de la variable. Pero yo no me acuerdo de lo que es.
Así que si sigue adelante -
¿Qué estaba diciendo? ¿Qué has traído para arriba?
[Estudiante] No mostrar añado - >> Yeah. Así que mostrar lo que puedo ayudar.
Si nos limitamos a mostrar i, se puso aquí cuál es el valor de i es
así que no tienes que imprimir cada vez.
Si seguimos adelante siguiente, vemos 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5.
Algo va mal, muy mal, y yo se pone a 0.
En cuanto a buggy4.c, vemos todo lo que sucede es array int [5];
for (i = 0; i <= sizeof (array); i + +)
array [i] = 0;
¿Qué es lo que vemos que está mal aquí?
Como una pista, cuando estaba haciendo el gdb buggy4 - vamos a romper la marcha principal, -
Yo tenía la impresión sizeof (array) sólo para ver lo que la condición es donde finalmente estallara.
¿Dónde estoy? ¿He correr?
Yo no declaró todavía.
Así imprimir sizeof (array) y eso es 20,
que se espera ya que mi matriz es de tamaño 5 y es de 5 enteros,
por lo que la cosa entera debe ser de 5 * sizeof (int) bytes, donde sizeof (int) tiende a ser 4.
Así sizeof (array) es 20.
¿Qué debería ser?
[Estudiante] Divididos por sizeof (int). >> Sí, / sizeof (int).
Parece que todavía hay un problema aquí. Creo que esto sólo debería ser <
ya que es casi siempre <nunca y <=.
Ahora vamos a pensar en por qué esto fue roto en realidad.
¿Alguien se adivina por qué se me pone a 0 a través de cada iteración del bucle?
La única cosa dentro de lo que está sucediendo aquí es que array [i] se establece en 0.
De alguna manera, esta línea de código que está causando nuestro int i que se establece en 0.
[Estudiante] ¿Podría ser porque está reemplazando la memoria de esta parte de i
cuando se piensa que es el siguiente elemento de la matriz? >> [Bowden] Sí.
Cuando vamos más allá del final de nuestra matriz,
de alguna manera que el espacio que estamos anulando está ignorando el valor de i.
Y si nos fijamos en buggy4, romper marcha principal,
vamos a imprimir la dirección de i.
Parece como si fuera bffff124.
Ahora vamos a imprimir la dirección de la matriz [0]. 110.
¿Qué pasa con [1]? 114.
[2], 118.
11c, 120. array [5] es bfff124.
Así array [5] tiene la misma dirección que yo, lo que significa que la matriz [5] es i.
Si tienen la misma dirección, son la misma cosa.
Así que cuando nos pusimos array [5] a 0, estamos i en 0.
Y si usted piensa acerca de esto en términos de la pila,
int i se declara en primer lugar, lo que significa que tiene un poco de espacio en la pila.
Entonces matriz [5] se asigna, por lo que luego de 20 bytes se asignan en la pila.
Así que se asignarán en primer lugar, a continuación, estos 20 bytes se asignan.
Así que pasa justo antes de la matriz,
y por el camino, como dije la semana pasada, cuando sea técnicamente la pila crece hacia abajo,
Cuando se indexa en una matriz, se nos garantiza que la posición de 0 ª de la matriz
siempre ocurre antes de la primera posición en la matriz.
Esto es un poco como lo hizo la semana pasada.
Observe que en la parte inferior tenemos la dirección 0 y en la parte superior tenemos Max dirección.
La pila siempre está creciendo hacia abajo.
Digamos que me asignen.
Asignamos entero i, lo que significa que vamos a decir aquí entero i se asignan.
Entonces asignamos nuestra matriz de 5 enteros, lo que significa que debajo de eso,
desde que la pila crece hacia abajo, esos 5 números enteros se asignan.
Sin embargo, debido a cómo funcionan las matrices, se tiene la garantía de que la primera posición en la matriz
siempre tiene una dirección inferior a la segunda cosa en la matriz.
Así posición 0 array siempre tiene que ocurrir primero en la memoria,
mientras que la posición de la matriz 1 tiene que suceder después de que
y posición de matriz 2 tiene que suceder después de eso,
lo que significa que la posición 0 array que sucedería en algún lugar por aquí,
posición de matriz 1 pasaría por encima de ese
porque mueve hacia arriba significa mayores direcciones desde la dirección máxima es de aquí.
Así array [0] hasta aquí, array [1] aquí, array [2] hasta aquí, array [3] hasta aquí.
Note como antes hemos destinado entero i todo el camino hasta aquí,
a medida que avanzamos más y más en nuestro arsenal, estamos cada vez más cerca y más cerca de nuestro entero i.
Lo que pasa es que array [5], que es una posición más allá de nuestra matriz,
es exactamente donde entero i pasó a ser asignado.
Así que ese es el punto en el que nos ha tocado estar golpeando el espacio en la pila
que se asignaron entero i, y estamos estableciendo que a 0.
>> Eso es lo que funciona. ¿Preguntas? Si.
[Estudiante] No importa. Bien.
[Estudiante] ¿Cómo evitar este tipo de errores?
Esta clase de errores? No utilice C como lenguaje de programación.
Use un lenguaje que tiene límites de la matriz de comprobación.
Siempre y cuando usted tiene cuidado, usted sólo tiene que evitar ir más allá de los límites de la matriz.
[Estudiante] Así que aquí cuando fuimos más allá de los límites de la matriz -
[Bowden] Ahí es donde las cosas empiezan a ir mal. >> [Estudiante] Oh, está bien.
Siempre y cuando se mantenga dentro de la memoria asignada para la matriz, estás bien.
Sin embargo, C no realiza comprobaciones de error. Si lo hago matriz [1000], que con mucho gusto basta con modificar pase lo que pase -
Se va al principio de la matriz, entonces se va después de 1000 posiciones y lo establece en 0.
No hace ninguna comprobación de que oh, esto en realidad no tiene 1000 cosas en él.
1000 es mucho más de lo que debería estar cambiando,
mientras que Java o algo que usted obtendrá la matriz fuera de límites índice
o el índice de excepción límites.
Es por eso que una gran cantidad de lenguajes de alto nivel tiene estas cosas
donde si usted va más allá de los límites de la matriz, que no vaya
de modo que usted no puede cambiar las cosas desde abajo que
y entonces las cosas van mucho peor de lo que acaba de conseguir una excepción
diciendo que iba más allá del final de la matriz.
[Estudiante] Y así lo hemos cambiado el <= a sólo <? >> [Bowden] Yeah.
Debe ser <sizeof (array) / sizeof (int);
desde sizeof (array) es de 20, pero solo queremos 5. >> [Estudiante] Derecho.
Más preguntas? Bien.
>> [Estudiante] Tengo una pregunta. Sí >>.
[Estudiante] ¿Cuál es la variable de matriz real?
[Bowden] Al igual que lo que es matriz?
Matriz en sí es un símbolo.
Es sólo la dirección de comienzo de los 20 bytes que se hace referencia.
Usted puede pensar en él como un puntero, pero es un puntero constante.
Tan pronto como las cosas se compila, la matriz de la variable ya no existe.
[Estudiante] Entonces, ¿cómo encontrar el tamaño de la matriz?
Tamaño de matriz se refiere al tamaño de ese bloque que hace referencia a ese símbolo.
Cuando hago algo como printf ("% p \ n", array);
vamos a correr.
¿Qué acabo de hacer el mal?
'Array' Matriz declarado aquí.
Oh, aquí arriba.
Clang es inteligente, y sucede a notar que yo declaré la matriz como 5 elementos
pero estoy en posición de indexación 1000.
Se puede hacer eso porque estos son sólo constantes.
Sólo se puede llegar a darse cuenta de que yo voy más allá de los límites de la matriz.
Pero note antes, cuando teníamos que ser incorrecta,
no es posible que determinar cuántos valores que podría asumir,
por lo que no puede determinar que i iba más allá del final de la matriz.
Eso es sólo Clang ser inteligente.
>> Pero ahora hacer buggy4. Entonces, ¿qué estoy haciendo mal?
Implícitamente se declara función de biblioteca 'printf'.
Voy a querer incluir # .
Bien. Ahora ejecuta buggy4.
Impresión del valor de la matriz como lo hice aquí, imprimirlo como un puntero
imprime algo que se parece a esto - bfb8805c - que es un poco de dirección
que está en la región de pila-ish.
Matriz en sí es como un puntero, pero no es un puntero real,
desde un puntero normal que podemos cambiar.
Array es sólo una constante. Los 20 bloques de memoria comenzará a las 0xbfb8805c dirección.
Así bfb8805c través de esta dirección +20--o supongo -20 -
es toda la memoria asignada a esta matriz.
Array, la propia variable no se almacena en cualquier lugar.
Cuando está compilando el compilador - ola mano en ella -
pero el compilador sólo se utilizan donde conoce matriz a ser.
Se sabe que la matriz se inicia,
y por lo que siempre puede hacer las cosas en términos de compensaciones de ese principio.
No se necesita una variable en sí para representar matriz.
Pero cuando hago algo como int * p = array, ahora que p es un puntero que apunta a la matriz,
y ahora p en realidad no existe en la pila.
Soy libre para cambiar p. Que puedo hacer p = malloc.
Por lo tanto, inicialmente señaló a la matriz, y ahora apunta a un cierto espacio en el montón.
No puedo hacer array = malloc.
Si Clang es inteligente, se me grites la derecha del palo.
De hecho, estoy bastante seguro de gcc lo haría también.
Así que tipo de matriz 'int [5]' no es asignable.
No se puede asignar algo a un tipo de matriz
porque la matriz es simplemente una constante.
Es un símbolo que hace referencia a esos 20 bytes. No lo puedo cambiar.
>> [Estudiante] ¿Y dónde está el tamaño de la matriz almacenada?
[Bowden] No se almacena en cualquier lugar. Es cuando está compilando.
Entonces, ¿dónde está el tamaño de la matriz almacenada?
Sólo se puede utilizar sizeof (array) dentro de la función que la matriz se haya declarado.
Así que si hago alguna función, foo, y lo hago (int array [])
printf ("% d \ n", sizeof (array));
y luego por aquí me llaman foo (array);
dentro de esta función - vamos a ejecutarlo.
Se trata de ser inteligente Clang nuevo.
Me está diciendo que sizeof parámetro de matriz en función
volverá tamaño de 'int'.
Esto sería un error si no es lo que yo quería que sucediera.
Vamos realmente apagar Werror.
Advertencia. Las advertencias están bien.
Todavía se compilará el tiempo que tiene una advertencia.
. / A.out va a imprimir 4.
La advertencia que se ha generado es un indicador claro de lo que salió mal.
Esta matriz int es sólo va a imprimir sizeof (int *).
Incluso si pongo matriz [5] aquí, sigue siendo sólo va a imprimir sizeof (int *).
Así que tan pronto como usted lo pasa a una función, la distinción entre matrices y punteros
es inexistente.
Esto pasa a ser una matriz que se declaró en la pila,
pero tan pronto como se pasa ese valor, que 0xbf bla, bla, bla, en esta función,
a continuación, este puntero apunta a la matriz en la pila.
Así que eso significa que sizeof sólo se aplica en la función que el conjunto fue declarado,
lo que significa que cuando se está compilando esta función,
Clang cuando pasa a través de esta función, ve matriz es una matriz de int tamaño 5.
Así lo ve sizeof (array). Bueno, eso es 20.
Eso es en realidad cómo sizeof básicamente funciona para casi todos los casos.
Sizeof no es una función, es un operador.
No llame a la función sizeof.
Sizeof (int), el compilador sólo la va a traducir a 4.
¿Lo tienes? Bien.
>> [Estudiante] ¿Cuál es la diferencia entre sizeof (array) de principal y en foo?
Esto se debe a que estamos diciendo sizeof (array), que es de tipo int *,
mientras que la matriz de aquí abajo no es de tipo int *, es una matriz int.
>> [Estudiante] Así que si usted tenía el parámetro de matriz [] en lugar de la matriz * int,
¿significa que todavía podría cambiar matriz porque ahora es un puntero?
[Bowden] De esta manera? >> [Estudiante] Yeah. ¿Se puede cambiar array dentro de la función ahora?
[Bowden] Usted podría cambiar la matriz en ambos casos.
En ambos casos, usted es libre de decir array [4] = 0.
[Estudiante] ¿Pero puede usted hacer punto matriz a otra cosa?
[Bowden] Oh. Si. En cualquiera de los casos - >> [estudiante] Yeah.
[Bowden] La distinción entre array [] y una matriz int *, no hay ninguno.
También puede obtener algunos matriz multidimensional aquí
por alguna sintaxis conveniente, pero todavía es sólo un indicador.
Esto quiere decir que soy libre de hacer array = malloc (sizeof (int)), y ahora apuntan a otro lugar.
Pero al igual que cómo esto funciona siempre y siempre,
cambiar esta matriz haciendo que apunte a otra cosa
no cambia esta matriz por aquí porque es una copia del argumento,
no es un puntero a ese argumento.
Y de hecho, así como la indicación más de que es exactamente lo mismo -
ya hemos visto lo que se imprime la matriz de impresión -
lo que si imprimimos la dirección de la matriz o la dirección de la dirección de la matriz
a cualquiera de esos?
Vamos a pasar por alto esto.
Bien. Esto está bien. Ahora está en marcha. / A.out.
Matriz de impresión, y luego imprimir la dirección de la matriz, son la misma cosa.
Matriz simplemente no existe.
Se sabe cuando va a imprimir array, que está imprimiendo el símbolo que se refiere a esos 20 bytes.
Impresión de la dirección de la matriz, así, la matriz no existe.
No tiene una dirección, por lo que sólo imprime la dirección de esos 20 bytes.
Tan pronto como se compila hacia abajo, como en su buggy4 compilado. / A.out,
matriz es inexistente.
Punteros existe. Las matrices no.
Los bloques de memoria que representa el conjunto todavía existe,
pero la matriz de variables y variables de ese tipo no existen.
Esos son como las principales diferencias entre las matrices y punteros
están tan pronto como usted haga llamadas a funciones, no hay ninguna diferencia.
Pero dentro de la función que la propia matriz se declara, sizeof funciona de forma diferente
ya que va a imprimir el tamaño de los bloques en lugar de el tamaño del tipo,
y no se puede cambiar porque es un símbolo.
Impresión de la cosa y la dirección de la cosa imprime la misma cosa.
Y eso es más o menos la misma.
[Estudiante] ¿Podría usted decir que una vez más?
Me podría haber pasado algo por alto.
Matriz de impresión y la dirección de la matriz imprime la misma cosa,
mientras que si se imprime un puntero frente a la dirección del puntero,
la única cosa que imprime la dirección de lo que estás señalando,
el otro imprime la dirección del puntero en la pila.
Puede cambiar un puntero, no se puede cambiar un símbolo de matriz.
Y puntero sizeof se va a imprimir el tamaño de ese tipo de puntero.
Así int * p sizeof (p) va a imprimir 4,
pero int array [5] impresión sizeof (array) se va a imprimir 20.
[Estudiante] Así int array [5] se imprimirán 20? Sí >>.
Es por eso que dentro de buggy4 cuando lo que solía ser sizeof (array)
esto hacía yo <20, que no es lo que queríamos.
Queremos i <5. >> [Estudiante] Bueno.
[Bowden] Y luego, tan pronto como usted comience a pasar en las funciones,
si lo hiciéramos int * p = array;
dentro de esta función, que básicamente puede utilizar p y matriz en exactamente las mismas formas,
excepto por el problema sizeof y el problema cambiando.
Pero p [0] = 1; es lo mismo que decir array [0] = 1;
Y tan pronto como decimos foo (array), o foo (p);
dentro de la función foo, esta es la misma llamada dos veces.
No hay ninguna diferencia entre estas dos llamadas.
>> Todo el mundo bueno en eso? Bien.
Tenemos 10 minutos.
>> Vamos a tratar de conseguir a través de este programa Typer Hacker,
Este sitio web, que salió el año pasado o algo así.
Es sólo supone que es como se escribe al azar y se imprime -
Cualquiera que sea el archivo que resulta haber cargado es lo que parece que está escribiendo.
Parece una especie de código del sistema operativo.
Eso es lo que queremos llevar a cabo.
Usted debe tener un binario ejecutable llamado hacker_typer
que lleva en un solo argumento, el archivo de "tipo hacker."
Ejecutar el ejecutable debe borrar la pantalla
y luego imprimir un carácter del fichero pasado como cada vez que el usuario pulsa una tecla.
Asi que cualquier tecla que pulse, se debe desechar y en su lugar muestra un personaje del archivo
que es el argumento.
Yo casi le dirá cuáles son las cosas que vamos a necesitar saber lo son.
Pero queremos revisar la biblioteca termios.
Nunca he utilizado esta biblioteca en toda mi vida, lo que tiene efectos muy mínimos.
Pero esto va a ser la biblioteca se puede utilizar para desechar el carácter que golpeó
cuando se está escribiendo en estándar pulg
Así hacker_typer.c, y vamos a querer incluir # .
En cuanto a la página del manual de termios - estoy adivinando que la terminal OS o algo así -
No sé cómo leerlo.
En cuanto a esto, se dice para incluir estos dos archivos, por lo que vamos a hacer eso.
>> Primero lo primero, queremos tomar en un solo argumento, que es el archivo que debe abrirse.
Entonces, ¿qué es lo que quiero hacer? ¿Cómo puedo comprobar que tengo un solo argumento?
[Estudiante] Si argc es igual. >> [Bowden] Yeah.
Así que si (argc = 2!) Printf ("Uso:% s [archivo para abrir]").
Así que ahora si me quedo, sin aportar un segundo argumento - oh, necesito la nueva línea -
verás que dice uso:. / hacker_typer,
y luego el segundo argumento debe ser el archivo que desea abrir.
¿Y ahora qué hago?
Quiero leer de este archivo. ¿Cómo puedo leer un archivo?
[Estudiante] Usted abrirlo primero. Sí >>.
Así fopen. ¿Qué fopen parece?
[Estudiante] Nombre de archivo. >> [Bowden] Nombre del archivo que va a ser argv [1].
[Estudiante] Y entonces, ¿qué quieres hacer con él, así que el - >> [Bowden] Yeah.
Así que si no se acordaba, se podía hacer fopen hombre,
donde va a ser un camino const char * donde ruta es nombre de archivo,
modo const char *.
Si por casualidad usted no recuerda qué modo es así, entonces usted puede buscar el modo.
En el interior de las páginas de manual, el carácter de barra es lo que puede utilizar para buscar las cosas.
Así que escribir / mode para buscar el modo.
ny N son lo que usted puede utilizar para desplazarse por las coincidencias de búsqueda.
Aquí se dice que los argumento modo apunta a una cadena
comenzando con una de las secuencias siguientes.
Así que r, archivo de texto abierto para la lectura. Eso es lo que quiero hacer.
Para leer, y quiero guardar eso.
La cosa va a ser un archivo *. Ahora, ¿qué es lo que quiero hacer?
Dame un segundo.
Bien. Ahora, ¿qué es lo que quiero hacer?
[Estudiante] Compruebe si es NULL. >> [Bowden] Yeah.
Cada vez que se abre un archivo, asegúrese de que usted es capaz con éxito para abrirlo.
>> Ahora quiero hacer esas cosas termios donde quiero leer primero la configuración actual
y salvar a aquellos en algo, entonces quiero cambiar mi configuración
tirar a la basura cualquier personaje que me escriba,
y luego quiero actualizar esos valores.
Y luego, al final del programa, quiero volver a mi configuración original.
Así que la estructura va a ser de termios tipo, y lo voy a querer a dos de ellos.
La primera de ellas va a ser mi current_settings,
y luego van a ser mis hacker_settings.
En primer lugar, voy a querer guardar la configuración actual,
entonces voy a querer actualizar hacker_settings,
y después camino al final de mi programa, quiero volver a la configuración actual.
Así que guardar la configuración actual, la forma en que funciona, termios hombre.
Vemos que tenemos este tcsetattr int, int tcgetattr.
Paso en una estructura termios por su puntero.
La forma en que se verá es - HE olvidado ya lo que la función fue llamada.
Copiar y pegar.
Así tcgetattr, entonces quiero pasar en la estructura que estoy ahorrando la información,
que va a ser current_settings,
y el primer argumento es el descriptor de archivo para lo que quiero para guardar los atributos de.
¿Qué es el descriptor de archivo es como cada vez que abra un archivo, se obtiene un descriptor de archivo.
Cuando fopen argv [1], se obtiene un descriptor de fichero que se hace referencia
cada vez que se desea leer o escribir en él.
Ese no es el descriptor de archivo que desea utilizar aquí.
Hay tres descriptores de archivo que tiene por defecto,
que son estándar, salida de estándar y el error estándar.
Por defecto, creo que es estándar en es 0, la salida estándar es 1, y el error estándar es de 2.
Entonces, ¿qué es lo que quiero cambiar la configuración de?
Quiero cambiar la configuración de cada vez que me tocó un personaje,
Quiero que tirar ese carácter inmediato en lugar de imprimirlo en la pantalla.
¿Qué corriente - standard in, out estándar o error estándar -
responde a las cosas cuando escribo en el teclado? >> [Estudiante] Standard pulg >> Yeah.
Así que puede hacer de 0 o yo podamos hacer stdin.
Me estoy poniendo el estándar de current_settings pulg
>> Ahora quiero actualizar esos valores,
lo primero que voy a copiar en hacker_settings lo que mis current_settings son.
¿Y cómo es el trabajo structs se acaba de copiar.
Esto copia todos los campos, como era de esperar.
>> Ahora quiero actualizar algunos de los campos.
En cuanto a termios, usted tendría que leer a través de una gran cantidad de este
sólo para ver lo que usted quiere buscar,
pero las banderas que usted va a querer tener en cuenta son eco,
así ECHO Echo caracteres de entrada.
En primer lugar quiero dejar - HE olvidado ya lo que los campos son.
Esto es lo que la estructura se parece.
Así modos de entrada Creo que desee cambiar.
Veremos la solución para asegurarse de que es lo que queremos cambiar.
Queremos cambiar lflag con el fin de evitar tener que buscar a través de todos estos.
Queremos cambiar los modos locales.
Usted tendría que leer toda esta cosa a entender que todo pertenece
que queremos cambiar.
Pero es en el interior de los modos locales donde vamos a querer cambiar eso.
Así hacker_settings.cc_lmode es lo que se llama.
c_lflag.
Aquí es donde nos metemos en los operadores bit a bit.
Estamos un poco fuera de tiempo, pero vamos a ir a través de él muy rápido.
Aquí es donde nos metemos en los operadores bit a bit,
donde creo que dije una vez hace mucho tiempo que cada vez que empezar a tratar con banderas,
usted va a estar utilizando el operador bit a bit mucho.
Cada bit de la bandera corresponde a algún tipo de comportamiento.
Así que aquí, este indicador tiene un montón de cosas diferentes, donde todos ellos significan algo diferente.
Pero lo que quiero hacer es apagar el bit que corresponde a ECHO.
Así que para apagar eso que hago y = ¬ ECHO.
En realidad, creo que es como techo o algo así. Voy a comprobar de nuevo.
Puedo termios. Es sólo ECHO.
ECHO va a ser un solo bit.
¬ ECHO va a significar que todos los bits están puestos a 1, lo que significa que todos los indicadores se establecen en true
excepto para el bit de ECHO.
Al poner fin a mis banderas cercanas con esto, significa todas las banderas que están establecidas actualmente en true
Todavía se establece en true.
Si mi bandera ECHO se establece en true, entonces este es necesariamente establece en false en la bandera ECHO.
Así que esta línea de código sólo se apaga el indicador de ECHO.
Las otras líneas de código, sólo voy a copiarlos en el interés de tiempo y luego explicarlos.
En la solución, dice 0.
Es probable que sea mejor decir explícitamente stdin.
>> Tenga en cuenta que también estoy haciendo ECHO | ICANON aquí.
ICANON se refiere a algo distinto, es decir, el modo canónico.
¿Qué significa el modo canónico es por lo general cuando se está escribiendo la línea de comandos,
estándar no procesa nada hasta que llegues nueva línea.
Así que cuando usted GetString, escribe un montón de cosas, entonces usted golpea nueva línea.
Fue entonces cuando se envía al estándar pulg
Ese es el valor predeterminado.
Cuando desactive el modo canónico, ahora cada personaje pulsa
es lo que se procesa, que suele ser un poco mal porque es lento para procesar estas cosas,
por lo que es bueno para amortiguar en líneas enteras.
Pero quiero que cada personaje para ser procesado
ya que no quiero que me esperes para golpear de nueva línea
antes de procesar todos los personajes que he estado escribiendo.
Esto desactiva el modo canónico.
Estas cosas sólo quiere decir cuando en realidad procesa caracteres.
Esto significa procesar de inmediato, tan pronto como les estoy escribiendo, procesarlos.
Y esta es la función que está actualizando mi configuración estándar para adentro,
y medios TCSA hacerlo ahora mismo.
Las otras opciones son esperar a que todo lo que se encuentra actualmente en la corriente se procesa.
Eso realmente no importa.
Justo en este momento puede cambiar mi configuración para ser lo que actualmente se encuentra en hacker_typer_settings.
Supongo que lo llamó hacker_settings, así que vamos a cambiar eso.
Cambiar todo para hacker_settings.
>> Ahora, al final de nuestro programa vamos a querer volver
a lo que es en la actualidad dentro de normal_settings,
que se va a ver igual y normal_settings.
Tenga en cuenta que no he cambiado ninguna de mis normal_settings ya que originalmente consiguiendo.
Entonces para simplemente cambiar de nuevo, les pasa de nuevo al final.
Esta fue la actualización. Bien.
>> Ahora dentro de aquí sólo voy a explicar el código en aras del tiempo.
No es que mucho código.
Vemos, leemos un carácter del archivo. Lo llamamos f.
Ahora usted puede el hombre fgetc, pero ¿cómo fgetc va a funcionar
sólo se va a devolver el carácter que usted acaba de leer o EOF,
que se corresponde con el final del archivo o algún suceso de error.
Estamos bucle, sin dejar de leer un solo carácter de los autos,
hasta que nos hemos quedado sin caracteres a leer.
Y ya que estamos haciendo eso, esperamos en un solo carácter de norma pulg
Cada vez que escriba algo en la línea de comandos,
que está leyendo en un personaje de nivel pulg
Entonces putchar es sólo va a poner el carbón leemos aquí arriba desde el archivo de salida estándar.
Puede putchar hombre, pero es sólo poner en la salida estándar, se imprime ese carácter.
También puede simplemente hacer printf ("% c", c); La misma idea.
Eso va a hacer la mayor parte de nuestro trabajo.
>> Lo último que vas a querer hacer es fclose nuestro archivo.
Si no fclose, eso es una pérdida de memoria.
Queremos fclose el archivo que se abrió originalmente, y creo que eso es todo.
Si hacemos eso, yo ya tengo problemas.
Vamos a ver.
¿Qué es lo que quejarse?
Se esperaba 'int' pero el argumento es de tipo 'struct _IO_FILE *'.
Vamos a ver si eso funciona.
Sólo permitido en C99. Augh. Está bien, hacer hacker_typer.
Ahora tenemos descripciones más útiles.
Por lo tanto el uso de identificador no declarado "normal_settings '.
Yo no lo llamó normal_settings. Lo llamé current_settings.
Así que vamos a cambiar todo eso.
Ahora pasa argumento.
Voy a hacer esto 0 por ahora.
Bien. . / Hacker_typer cp.c.
Yo también no borrar la pantalla al principio.
Pero usted puede mirar hacia atrás al conjunto último problema para ver cómo limpiar la pantalla.
Es simplemente imprimir algunos caracteres
mientras que esto está haciendo lo que quiero hacer.
Bien.
Y pensando en por qué esto tenía que ser 0 en lugar de la entrada estándar,
que debe ser # define 0,
este se queja de que -
Antes, cuando he dicho que no hay descriptores de archivo, pero luego también tienes tu * FILE,
un descriptor de archivo es sólo un único número entero,
mientras que un FILE * tiene un montón de cosas asociada a ella.
La razón por la que tenemos que decir 0 en lugar de la entrada estándar
es que stdin es un FILE * que apunta a lo que hace referencia descriptor de archivo 0.
Así que hasta aquí cuando hago fopen (argv [1], me estoy poniendo un * archivo de nuevo.
Pero en algún lugar en que * FILE es una cosa que corresponde al descriptor de archivo para el archivo.
Si nos fijamos en la página del manual de abierto, así que creo que tendré que hacer man 3 abierto - No. -
man 2 abierto - sí.
Si nos fijamos en la página de abierto, abierto es como un fopen de nivel inferior,
y se devuelve el descriptor de archivo real.
fopen hace un montón de cosas sobre abierto,
que en lugar de devolver sólo el descriptor de fichero devuelve un archivo completo puntero *
dentro de los cuales es nuestro descriptor de archivo pequeño.
Así estándar se refiere a la cosa * FILE,
mientras que 0 se refiere sólo a la norma descriptor de fichero en sí mismo.
>> ¿Preguntas?
[Risas] sopló a través de eso.
Está bien. Hemos terminado. [Risas]
>> [CS50.TV]