[Powered by Google Translate] [Sección 4 - Más Cómodo]
[Rob Bowden - Harvard University]
[Esta es CS50. - CS50.TV]
Tenemos un futuro examen, en caso de que ustedes no lo sabía.
Es, básicamente, en todo lo que podría haber visto en clase o deberían haber visto en clase.
Eso incluye a los punteros, a pesar de que es un tema muy reciente.
Al menos debe comprender los altos niveles de las mismas.
Todo lo que pasase en la clase que usted debe entender para el concurso.
Así que si usted tiene preguntas sobre ellos, usted puede pedirles ahora.
Pero esto va a ser una sesión muy dirigida por los estudiantes donde ustedes hacer preguntas,
así que espero que la gente tiene preguntas.
¿Alguien tiene alguna pregunta?
Sí. >> [Estudiante] ¿Se puede pasar punteros de nuevo?
Voy a ir más punteros.
Todas las variables necesariamente viven en la memoria,
pero por lo general usted no se preocupe por eso y sólo dices x + 2 y + 3 y
y el compilador se darán cuenta de que las cosas se están viviendo por ti.
Una vez que usted está tratando con punteros, ahora que está explícitamente usando esas direcciones de memoria.
Así que una sola variable sólo alguna vez vivir en una sola dirección en un momento dado.
Si queremos declarar un puntero, lo que el tipo va a parecer?
>> Quiero declarar un puntero p. ¿Qué significa el tipo parece?
[Estudiante] int * p. Sí >>. Así int * p.
¿Y cómo puedo hacer que apunte a x? >> [Estudiante] Ampersand.
[Bowden] Así es, literalmente, llama ampersand la dirección del operador.
Así que cuando digo x y se está haciendo la dirección de memoria de la variable x.
Así que ahora tengo el puntero p, y en cualquier parte de mi código que pueda usar p *
o podría usar x, y será exactamente lo mismo.
(* P). ¿Qué hace esto? ¿Qué hace que la estrella de decir?
[Estudiante] Se refiere a un valor en ese punto. Sí >>.
Así que si nos fijamos en él, puede ser muy útil para extraer los diagramas
donde se trata de una pequeña caja de memoria para x, que pasa a tener el valor 4,
entonces tenemos una pequeña caja de memoria para p,
por lo que p apunta a x, por lo que dibujar una flecha de p para x.
Así que cuando decimos que p * que estamos diciendo vaya a la caja que es p.
Star es seguir la flecha y luego hacer lo que quieras con esa caja ahí.
Así que puedo decir * p = 7, y que irá a la caja que es x y el cambio que a 7.
O podría decir z = int * p * 2; Eso es confuso, ya que la estrella, estrella.
La estrella se desreferencia p, la otra estrella se multiplica por 2.
Tenga en cuenta que podría tener tan bien sustituir el p * con x.
Usted las puede utilizar de la misma manera.
Y más tarde que puede tener el punto P a una cosa completamente nueva.
Yo sólo puedo decir p = &z;
Así que ahora ap ya no lleva a x, sino que apunta a z.
Y cada vez que hago p * es lo mismo que hacer z.
Así que lo útil de esto es que una vez que comienza a recibir en funciones.
>> Es un poco inútil para declarar un puntero que apunta a algo
y si uno la eliminación de referencias
cuando se podría haber utilizado la variable original, para empezar.
Pero cuando te metes en funciones - así que vamos a decir que tenemos alguna función, int foo,
que toma un puntero y sólo hace * p = 6;
Como vimos antes, con swap, no se puede hacer una permuta de efectivo y una función separada
con sólo pasar enteros porque todo en C siempre pasa por valor.
Incluso cuando estás pasando punteros estás pasando por valor.
Lo que pasa es que esos valores son direcciones de memoria.
Así que cuando digo foo (p), estoy pasando el puntero a la función foo
y luego foo está haciendo * p = 6;
Así que dentro de esa función, * p sigue siendo equivalente a x,
pero no puedo usar x dentro de esa función, porque no es de ámbito dentro de esa función.
Así que * p = 6 es la única forma en que puede acceder a una variable local desde otra función.
O, bueno, los punteros son la única forma en que puede acceder a una variable local desde otra función.
[Estudiante] Vamos a decir que quería devolver un puntero. ¿Exactamente cómo se hace eso?
[Bowden] Devuelve un puntero como en algo como int y = 3; & y vuelta? >> [Estudiante] Yeah.
[Bowden] Bueno. Usted nunca debe hacer esto. Esto es malo.
Creo que he visto en estas diapositivas de conferencias que comenzó a ver este diagrama completo de la memoria
donde hasta aquí tienes la dirección de memoria 0
y aquí tienes la dirección de memoria 4 gigas o 2 a la 32.
Así que tienes algunas cosas y algunas cosas y luego tienes tu stack
y tienes tu montón, que acaba de comenzar a aprender acerca de, creciendo.
[Estudiante] ¿No es el montón encima de la pila?
>> Si. El montón está arriba, ¿no? >> [Estudiante] Bueno, poner 0 en la parte superior.
[Estudiante] Oh, poner 0 en la parte superior. >> [Estudiante] Oh, está bien.
Descargo de responsabilidad: En cualquier lugar con CS50 vas a ver de esta manera. >> [Estudiante] Bueno.
Es sólo que cuando estás viendo primero las pilas,
como cuando usted piensa en una pila piensas de apilar cosas encima de la otra.
Por lo tanto, tienden a girar en torno a este por lo que la pila está creciendo como una pila de costumbre
en lugar de la pila colgando hacia abajo. >> [Los estudiantes] no montones técnicamente crecer demasiado, sin embargo?
Depende de lo que entiendas por crecer.
La pila y montón siempre crecer en direcciones opuestas.
Una pila siempre está creciendo en el sentido de que está creciendo
al aumento de las direcciones de la memoria y el montón crece hacia abajo
en que está creciendo hacia direcciones bajas de memoria.
Así que la parte superior es 0 y la parte inferior es de alta dirección de memoria.
Los dos están en crecimiento, sólo en direcciones opuestas.
[Estudiante] Yo sólo quería decir que porque usted ha dicho que poner la pila en la parte inferior
porque parece más intuitivo porque para la pila para empezar en la parte superior de un montón,
pila está por encima de sí mismo también, así que eso es - >> Yeah.
También pensar en el montón como crecía y más grande, pero la pila más.
Así que la pila es la que nos tipo de ganas de mostrar su crecimiento.
Pero en todas partes se mira de otra manera se va a mostrar la dirección 0 en la parte superior
y la dirección de memoria más alta en la parte inferior, por lo que este es su vista habitual de la memoria.
>> ¿Tiene una pregunta?
[Estudiante] ¿Puedes decirnos más sobre el montón?
Si. Voy a llegar a eso en un segundo.
En primer lugar, ¿por qué volver a volver y que y es una cosa mala,
en la pila tiene un montón de marcos de pila que representan todas las funciones
que han sido llamados.
Así que haciendo caso omiso de las cosas anteriores, la parte superior de la pila siempre va a ser la función principal
ya que es la primera función que está siendo llamado.
Y luego, cuando se llama a otra función, la pila va a crecer hacia abajo.
Así que si llamo a alguna función, foo, y se pone su propio marco de pila,
puede llamar a alguna función, bar, sino que obtiene su propio marco de pila.
Y bar podía ser recursiva y puede llamarse a sí misma,
y para que la segunda llamada a la barra se va a poner su propio marco de pila.
Y así, lo que sucede en estos marcos de pila son todas las variables locales
y todos los argumentos de la función que -
Las cosas que son de ámbito local a esta función vaya en estos marcos de pila.
Eso quiere decir que cuando me dijo algo así como bar es una función,
Sólo voy a declarar un entero y luego devolver un puntero a ese entero.
Entonces, ¿dónde y vivir?
[Estudiante] y vive en el bar. >> [Bowden] Yeah.
En algún lugar de esta pequeña plaza de la memoria es una plaza más escasos y que tiene en ella.
Cuando regrese & y, estoy devolviendo un puntero a este pequeño bloque de memoria.
Pero cuando una función retorna, su marco de pila se extrae de la pila.
Y es por eso que se llama pila.
Es como si la estructura de datos pila, si sabes lo que es.
O incluso como una pila de bandejas es siempre el ejemplo,
principal va a ir en la parte inferior, la función primera vez que llame se va a ir por encima de eso,
y no se puede volver a principal hasta que regrese de todas las funciones que han sido llamados
que han sido colocados en la parte superior de la misma.
>> [Estudiante] Así que si usted hizo volver la y &, ese valor está sujeto a cambios sin previo aviso.
Sí, es - >> [estudiante] Podría ser sobreescrito. Sí >>.
Es completamente - Si intenta -
Este sería también un bar * int porque está devolviendo un puntero,
por lo que su tipo de retorno es int *.
Si intenta utilizar el valor de retorno de esta función, es un comportamiento indefinido
porque ese puntero apunta al mal recuerdo. >> [Estudiante] Bueno.
¿Y qué si, por ejemplo, declaró int * y = malloc (sizeof (int))?
Eso está mejor. Sí.
[Estudiante] Hablamos de cómo las cosas cuando arrastramos a nuestra papelera de reciclaje
no están realmente borrado, nosotros sólo pierden sus punteros.
Así que en este caso es lo que realmente borrar el valor o todavía hay en la memoria?
En su mayor parte, va a estar todavía allí.
Pero digamos que nos pasó a llamar a otra función, baz.
Baz se va a poner su propio marco de pila en aquí.
Va a ser sobrescribir todas estas cosas,
y si luego tratar de utilizar el puntero que obtuvo antes,
no va a ser el mismo valor.
Va a haber cambiado sólo porque se llama el baz función.
[Estudiante] Pero si no nosotros, que seguimos recibiendo 3?
[Bowden] Lo más probable es que sí.
Pero no se puede confiar en eso. C sólo dice un comportamiento indefinido.
>> [Estudiante] Oh, sí. Bien.
Así que cuando usted desea devolver un puntero, aquí es donde viene malloc en uso.
Estoy escribiendo en realidad sólo devuelve malloc (3 * sizeof (int)).
Vamos a repasar malloc más en un segundo, pero es la idea de malloc todas sus variables locales
siempre van a la pila.
Cualquier cosa que malloced va a la pila, y será por siempre y siempre estar en el montón
hasta que explícitamente se lo libere.
Así que esto significa que cuando malloc algo, que va a sobrevivir después de que la función regrese.
[Estudiante] ¿Va a sobrevivir después de que el programa deje de funcionar? No. >>
Muy bien, así que va a estar allí hasta que el programa es todo el camino hecho correr. Sí >>.
Podemos repasar los detalles de lo que sucede cuando el programa deja de ejecutarse.
Es posible que necesite que me recuerde, pero eso es una cosa separada del todo.
[Estudiante] Así malloc crea un puntero? Sí >>.
Malloc - >> [estudiante] Creo que malloc designa un bloque de memoria que puede utilizar un puntero.
[Bowden] Quiero ese diagrama nuevo. >> [Estudiante] Así funciona esta función, sin embargo?
[Estudiante] Sí, malloc designa un bloque de memoria que puede utilizar,
y, a continuación, devuelve la dirección del primer bloque de esa memoria.
>> [Bowden] Yeah. Así que cuando malloc, que está agarrando un poco de bloque de memoria
que está actualmente en el montón.
Si la pila es demasiado pequeño, entonces el montón es sólo va a crecer, y que crece en esta dirección.
Así que digamos que el montón es demasiado pequeño.
Luego se trata de hacer crecer un poco y devolver un puntero a este bloque que acaba de crecer.
Cuando cosas gratis, que está haciendo más espacio en el montón,
Así que una tarde llamar a malloc puede reutilizar la memoria que había liberado previamente.
Lo importante de malloc y libre es lo que le da un control completo
durante la vida útil de estos bloques de memoria.
Las variables globales son siempre viva.
Las variables locales están vivos dentro de su ámbito de aplicación.
Tan pronto como se pasa de una llave, las variables locales están muertos.
Memoria Malloced está vivo cuando usted quiere que sea vivo
y luego se libera cuando se lo dices a ser puesto en libertad.
Esas son realmente las únicas tres tipos de memoria, la verdad.
Hay gestión de memoria automática, que es la pila.
Las cosas suceden de forma automática.
Cuando dices int x, se asigna memoria para x int.
Cuando x está fuera de ámbito, la memoria se recupera para x.
Luego está la gestión de memoria dinámica, que es lo que malloc es,
que es cuando usted tiene el control.
Usted decide cuándo dinámicamente la memoria debe y no debe ser asignado.
Y luego está estática, lo que significa que sólo vive para siempre,
que es lo que son las variables globales.
Sólo están siempre en la memoria.
>> ¿Preguntas?
[Estudiante] ¿Se puede definir un bloque sólo mediante el uso de llaves
pero no tener que tener una? if o while o algo por el estilo
Se puede definir como un bloque en una función, pero que tiene las llaves también.
[Estudiante] Por lo que no se puede tener como un par de llaves al azar en el código
que tiene variables locales? >> Sí, se puede.
En el interior de la barra int podríamos tener {int y = 3;}.
Que se supone que es justo aquí.
Pero eso define completamente el alcance de int y.
Después de que la segunda llave, y no se puede utilizar más.
Casi nunca hago eso, sin embargo.
Volviendo a lo que sucede cuando un programa termina,
hay una especie de mentira error / medio que le damos con el fin de hacer sólo las cosas más fáciles.
Les decimos que cuando se asigna memoria
que está asignando parte pedazo de RAM para esa variable.
Pero usted no está realmente en contacto directo con RAM nunca en sus programas.
Si se piensa en ello, ¿cómo dibujé -
Y de hecho, si usted va a través de GDB verá la misma cosa.
Sin importar cuántas veces se ejecuta el programa o qué programa está funcionando,
la pila siempre va a comenzar -
siempre vas a ver las variables alrededor de algo oxbffff dirección.
Por lo general, en algún lugar de esa región.
Pero, ¿cómo puede posiblemente tener 2 programas punteros a la misma memoria?
[Estudiante] Hay un poco de designación arbitraria de oxbfff donde se supone que debe estar en la RAM
que en realidad puede estar en diferentes lugares dependiendo de cuando la función fue llamada.
Si. El término es la memoria virtual.
La idea es que cada proceso, cada programa que se ejecuta en su computadora
tiene su propio - supongamos - 32 bits espacio de direcciones completamente independiente.
Este es el espacio de direcciones.
Tiene sus propios completamente independientes 4 gigabytes para su uso.
>> Así que si usted ejecuta dos programas al mismo tiempo, este programa de 4 gigabytes ve a sí mismo,
Este programa de 4 gigabytes ve a sí mismo,
y es imposible que este programa para eliminar la referencia de un puntero
y acabar con la memoria de este programa.
¿Y qué es la memoria virtual es una asignación de un espacio de direcciones de procesos
a las cosas reales de RAM.
Así que depende de su sistema operativo para saber que,
hey, cuando este tipo oxbfff desreferencia puntero, que realmente significa
que quiere byte RAM 1000,
mientras que si este programa oxbfff desreferencia, que realmente quiere byte RAM 10000.
Pueden ser arbitrariamente lejos.
Esto es cierto incluso de las cosas dentro de un espacio de direcciones de procesos único.
Así que, como ve los 4 gigabytes a sí mismo, pero vamos a decir -
[Estudiante] ¿Cada proceso individual -
Digamos que usted tiene una computadora con sólo 4 GB de RAM.
¿Tiene cada proceso ve los 4 gigabytes enteros? Sí >>.
Pero los 4 gigabytes que ve es una mentira.
Es sólo que piensa que tiene todo esto de memoria, ya que no conozco a ningún otro proceso existe.
Sólo se utilizará la memoria todo lo que realmente necesita.
El sistema operativo no se va a dar a este proceso RAM
si no se utiliza ninguna memoria en toda esta región.
No va a darle la memoria de esa región.
Pero la idea es que - Estoy tratando de pensar - No puedo pensar en una analogía.
Las analogías son difíciles.
Uno de los temas de la memoria virtual o una de las cosas que está resolviendo
es que los procesos deben ser completamente inconsciente de uno al otro.
Y para que pueda escribir cualquier programa que acaba de desreferencia cualquier puntero,
como acaba de escribir un programa que dice * (ox1234),
y que la memoria dereferencing dirección 1234.
>> Pero depende del sistema operativo para traducir Entonces, ¿qué significa 1234.
Así que si 1234 resulta ser una dirección de memoria válida para este proceso,
como si estuviera en la pila o algo así, entonces esto va a devolver el valor de esa dirección de memoria
por lo que el proceso sabe.
Pero si 1234 no es una dirección válida, al igual que ocurre a la tierra
en algún pedacito de memoria aquí que está más allá de la pila y el montón más allá
y no se ha utilizado realmente eso, entonces es cuando usted consigue cosas como segfaults
porque estás tocando de memoria que no se debe tocar.
Esto también es cierto -
Un sistema de 32-bits, 32 bits significa que usted tiene 32 bits para definir una dirección de memoria.
Es por eso que los punteros son 8 bytes ya que 32 bits son 8 bytes - o 4 bytes.
Los punteros son 4 bytes.
Así que cuando usted ve un puntero como oxbfffff, que es -
Dentro de cualquier programa dado que sólo puede construir cualquier puntero arbitrario,
en cualquier lugar de ox0 buey a 8 f's - FFFFFFFF.
[Estudiante] ¿No dicen que son 4 bytes? Sí >>.
[Estudiante] Luego, cada byte tendrá - >> [Bowden] Hexadecimal.
Hexadecimal - 5, 6, 7, 8. Así punteros que vas a ver siempre en hexadecimal.
Es sólo la forma en que clasificar punteros.
Cada 2 dígitos hexadecimal es de 1 byte.
Así que va a ser de 8 dígitos hexadecimales de 4 bytes.
Así, cada indicador individual en un sistema de 32-bit que va a ser de 4 bytes,
lo que significa que en el proceso se puede construir cualquier arbitrarias 4 bytes
y hacer un puntero fuera de él,
lo que significa que la medida de lo que es consciente, se puede tratar un entero 2 a los 32 bytes de memoria.
A pesar de que en realidad no se tiene acceso a que,
aunque el equipo sólo tiene 512 megabytes, que piensa que tiene suficiente memoria.
Y el sistema operativo es lo suficientemente inteligente que sólo asignar lo que realmente necesita.
No sólo tiene que ir, oh, un nuevo proceso: 4 gigas.
>> Si. >> [Estudiante] ¿Qué hace el buey decir? ¿Por qué lo escribió?
Es sólo el símbolo de hexadecimal.
Cuando usted ve un número de inicio con el buey, las cosas son sucesivas hexadecimal.
[Estudiante] Usted estaba explicando acerca de lo que sucede cuando un programa termina. Sí >>.
¿Qué sucede cuando un programa termina es el sistema operativo
sólo borra las asignaciones que tiene para estas direcciones, y eso es todo.
El sistema operativo puede simplemente dar ese recuerdo a otro programa para utilizarlo.
[Estudiante] Bueno.
Así que cuando usted asigna algo en el montón o las variables de pila o global ni nada,
todos ellos sólo desaparecen tan pronto como termine el programa
porque el sistema operativo es ahora libre para dar que la memoria a cualquier otro proceso.
[Estudiante] A pesar de que es probable que haya todavía valores escritos en? Sí >>.
Los valores son probablemente todavía allí.
Es sólo que va a ser difícil llegar a ellos.
Es mucho más difícil llegar a ellos de lo que es llegar a un archivo eliminado
debido a que el tipo de archivo borrado se sienta allí durante mucho tiempo y el disco duro es mucho más grande.
Así que va a sobrescribir las diferentes partes de la memoria
antes de que suceda para sobrescribir el bloque de memoria que utiliza ese archivo para estar en.
Pero la memoria principal, memoria RAM, se desplaza por mucho más rápido,
así que va a ser muy rápidamente sobrescrito.
Las preguntas sobre esta o cualquier otra cosa?
[Estudiante] Tengo preguntas sobre un tema diferente. >> Okay.
¿Alguien tiene alguna duda sobre esto?
>> Bien. Tema diferente. >> [Estudiante] Bueno.
Yo estaba pasando por algunas de las pruebas de la práctica,
y en una de ellas se refería a la sizeof
y el valor que devuelve o diferentes tipos de variables. Sí >>.
Y dijo que tanto int y largo plazo tanto el retorno 4, por lo que ambos son 4 bytes de longitud.
¿Hay alguna diferencia entre un int y un largo, o es lo mismo?
Sí, hay una diferencia.
El estándar de C -
Probablemente voy a meter la pata.
El estándar de C es igual que lo que C es la documentación oficial de C.
Esto es lo que dice.
Así que el estándar de C sólo dice que será un char para siempre y será siempre 1 byte.
Todo después de que - un corto es siempre acaba de definir como siendo mayor que o igual a un char.
Esto podría ser estrictamente mayor que, pero no positivo.
Un int se acaba de definir como siendo mayor que o igual a un corto.
Y una larga se acaba de definir como siendo mayor que o igual a un entero.
Y un largo tiempo es mayor que o igual a un tiempo.
Así que lo único que el estándar de C define es el orden relativo de todo.
La cantidad real de memoria que las cosas sigan por lo general hasta la ejecución,
pero está bastante bien definido en este punto. >> [Estudiante] Bueno.
Así que los pantalones cortos son casi siempre va a ser de 2 bytes.
Ints son casi siempre va a ser de 4 bytes.
Largos largos son casi siempre va a ser de 8 bytes.
Y anhela, depende de si usted está usando un 32-bit o un sistema de 64-bit.
Así un largo va a corresponder con el tipo de sistema.
Si usted está usando un sistema de 32-bit como el Appliance, que va a ser de 4 bytes.
Si usted está usando un 64-bit como un montón de computadoras recientes, que va a ser de 8 bytes.
>> Ints son casi siempre 4 bytes en este punto.
Largos largos son casi siempre de 8 bytes.
En el pasado, ints solía ser sólo 2 bytes.
Pero nótese que esto satisface completamente todas estas relaciones de superior e igual a.
Mientras está perfectamente autorizado a tener el mismo tamaño como un entero,
y también se les permite tener el mismo tamaño como un largo tiempo.
Y da la casualidad de que esté en 99,999% de los sistemas, que va a ser igual a
un int o un largo plazo. Sólo depende de 32-bit o 64-bit. >> [Estudiante] Bueno.
En carrozas, ¿cómo está el punto decimal designado en términos de bits?
Al igual que como binario? Sí >>.
No es necesario saber que para CS50.
Ni siquiera enterarse de que en el 61.
No se aprende que realmente en cualquier curso.
Es sólo una representación.
Me olvido de las asignaciones de bits exactas.
La idea de punto flotante es que se puede asignar un número específico de bits para representar -
Básicamente, todo está en notación científica.
Así que asignar un número específico de bits para representar el número en sí, al igual que 1,2345.
Nunca puede representar un número con más dígitos de los que 5.
Entonces también asignar un número específico de bits de modo que tiende a ser como
sólo se puede subir a un cierto número, como que es el mayor exponente que puede tener,
y sólo se puede bajar a un cierto exponente,
como que es el más pequeño exponente que puede tener.
>> No me acuerdo de los bits de forma exacta se asignan a todos estos valores,
pero un cierto número de bits se dedica a 1,2345,
otro cierto número de bits se dedica a la exponente,
y es sólo posible representar un exponente de un cierto tamaño.
[Estudiante] Y un doble? ¿Es como un float extra larga? Sí >>.
Es lo mismo que un flotador excepto que ahora está utilizando 8 bytes en lugar de 4 bytes.
Ahora podrás utilizar 9 cifras o dígitos 10,
y éste será capaz de subir a 300 en lugar de 100. >> [Estudiante] Bueno.
Y flotadores son también 4 bytes. Sí >>.
Bueno, de nuevo, probablemente lo general depende de la aplicación general,
pero los flotadores son de 4 bytes, los dobles son 8.
Dobles se llama doble, ya que son el doble del tamaño de la flota.
[Estudiante] Bueno. Y hay dos dobles? >> No hay.
Creo que - >> [estudiante] Al igual que anhela largas? Sí >>. No lo creo. Sí.
[Estudiante] El ensayo del año pasado había una pregunta acerca de la función principal
tener que ser parte de su programa.
La respuesta fue que no tiene por qué ser parte de su programa.
¿En qué situación? Eso es lo que vi.
[Bowden] Parece - >> [estudiante] ¿Qué situación?
¿Tiene el problema? >> [Estudiante] Sí, lo puedo tirar de él hacia arriba.
No tiene por qué ser, técnicamente, pero básicamente va a ser.
[Estudiante] Yo vi uno en una de año diferente.
Era como Verdadero o Falso: Un válido - >> Oh, un archivo c.?
. [Estudiante] Cualquier archivo c debe tener - [ambos términos a la vez - ininteligible]
Bien. Así que eso es aparte.
>> Un archivo. C sólo necesita contener funciones.
Usted puede compilar un archivo en código máquina, binario, lo que sea,
sin que sea ejecutable todavía.
Un ejecutable válido debe tener una función principal.
Usted puede escribir 100 funciones en 1 archivo pero no principales
y luego compilar que a binario,
entonces usted escribir otro archivo que sólo tiene principal tiene que llamar a un montón de estas funciones
en el archivo binario aquí.
Así que cuando usted está haciendo el ejecutable, que es lo que hace el enlazador
Se combina estos dos archivos binarios en un archivo ejecutable.
Así que un archivo. C no necesita tener una función principal en absoluto.
Y en grandes bases de código verás miles de archivos. C y 1 archivo principal.
Más preguntas?
[Estudiante] Hubo otra pregunta.
Dijo que hacer es un compilador. Verdadero o Falso?
Y la respuesta era falsa, y entendí por qué no es como Clang.
Pero, ¿cómo llamamos a hacer si no?
Hacer es básicamente - Puedo ver exactamente lo que lo llama.
Pero simplemente ejecuta los comandos.
Make.
Yo puedo tirar esto. Si.
Oh, sí. Asegúrese también lo hace.
Esto dice que el propósito de la utilidad make es determinar automáticamente
qué piezas de un programa necesitan ser recompilados
y emitir las órdenes para compilar.
Usted puede hacer que los archivos que son absolutamente enorme.
Hacer mira las marcas de tiempo de los archivos y, como hemos dicho antes,
puede compilar archivos individuales hacia abajo, y no es hasta que llegue al enlazador
que están juntas en un ejecutable.
Así que si usted tiene 10 diferentes archivos y realizar un cambio en una de ellas,
entonces, ¿qué marca va a hacer es recompilar que 1 archivo
y luego volver a vincular todo junto.
Pero es mucho más tonto que eso.
Depende de usted para definir completamente que eso es lo que debería estar haciendo.
Es por defecto tiene la capacidad de reconocer estas cosas sello de tiempo,
pero usted puede escribir un archivo make para hacer nada.
Usted puede escribir un archivo para hacer que al escribir lo hacen sólo cd a otro directorio.
Estaba frustrado porque todo tachuela interior de mi Appliance
y luego ver el PDF desde el Mac.
>> Así que ir a Finder y puedo ir a hacer, Conectar al servidor,
y el servidor me conecto a mi es Appliance, y luego abrir el PDF
que se compila por LaTeX.
Pero yo estaba frustrado porque cada vez que tenía que actualizar el PDF,
Tuve que copiar a un directorio específico que pueda acceder
y se hacía molesto.
Así que en vez escribí un archivo de marca, que tiene que definir la forma en que hace las cosas.
¿Cómo hacer que en esto es PDF LaTeX.
Al igual que cualquier otro archivo de marca - o supongo que no has visto los archivos Make,
pero tenemos en el aparato un archivo de marca global que sólo dice:
si está compilando un archivo de C, utilice Clang.
Y aquí en mi archivo de marca que hago yo digo,
el archivo que vas a querer compilar con PDF LaTeX.
Y lo que es LaTeX PDF que está haciendo la compilación.
Hacer no está compilando. Es simplemente ejecutar estos comandos en la secuencia que se especifica.
Así se ejecuta LaTeX PDF, lo copia al directorio que desea que se copian,
que el cd en el directorio y hace otras cosas,
pero lo único que hace es reconocer cuando cambia un archivo,
y si cambia, entonces se ejecutan los comandos que se supone para funcionar
cuando cambia el archivo. >> [Estudiante] Bueno.
No sé donde están los archivos make globales son para mí comprobar que funciona.
Otras preguntas? Cualquier cosa del pasado pruebas? Las cosas puntero?
Hay cosas sutiles con punteros como -
Yo no voy a ser capaz de encontrar una pregunta de cuestionario en él -
pero al igual que este tipo de cosas.
Asegúrese de entender que cuando digo int * x * y -
Esto no es exactamente nada aquí, supongo.
Pero al igual que * x * y, esos son dos variables que están en la pila.
Cuando digo x = malloc (sizeof (int)), x es todavía una variable en la pila,
malloc algún bloque más en el montón, y vamos a tener el punto x al montón.
>> Así que algo sobre los puntos de la pila a la pila.
Siempre que malloc nada, usted está inevitablemente guardarla dentro de un puntero.
Así que el puntero está en la pila, el bloque malloced está en el montón.
Muchas personas se confunden y dicen int * x = malloc, x está en el montón.
No. Lo que señala es x en el montón.
x sí mismo está en la pila, salvo que por la razón que sea que haya x una variable global,
en cuyo caso pasa a ser en otra región de memoria.
Así que hacer el seguimiento, estos diagramas de caja y la flecha son muy comunes para la prueba.
O si no está en concurso de 0, se estará a quiz 1.
Usted debe saber todo esto, los pasos de compilación
ya que tuvo que responder a preguntas sobre ellas. Sí.
[Estudiante] ¿Podemos repasar los pasos - >> Claro.
Antes de los pasos y la compilación tenemos preprocesamiento,
compilar, organizar y vincular.
Preprocesamiento. ¿Qué significa eso?
Es el paso más fácil en - bueno, no como -
eso no significa que debería ser obvio, pero es el paso más fácil.
Ustedes podrían implementar ustedes mismos. Si.
[Estudiante] Tome lo que usted tiene en su incluye como este y lo copia y luego define también.
Se ve las cosas como # include y # define,
y sólo copia y pega lo que los que realmente significan.
Así que cuando dices # include cs50.h, el preprocesador es copiar y pegar cs50.h
en esa línea.
Cuando dices # define x para ser 4, el preprocesador atraviesa todo el programa
y sustituye a todos los casos de x con 4.
Así que el preprocesador toma un archivo de C válido y envía un archivo válido de C
donde las cosas se han copiado y pegado.
Así que ahora compilar. ¿Qué significa eso?
[Estudiante] Va de C a binario.
>> [Bowden] No va todo el camino a binario.
[Estudiante] a código de máquina, entonces? >> No es código máquina.
[Estudiante] Asamblea? >> Asamblea.
Se va a la Asamblea antes de que va todo el camino a código C,
y la mayoría de lenguajes hacer algo como esto.
Elija cualquier lenguaje de alto nivel, y, si lo vas a compilar
lo más probable es que compile en pasos.
En primer lugar se va a compilar Python a C, entonces va a compilar C a la Asamblea,
y luego la Asamblea va a ser traducido a binario.
Así que compilar lo va a traer de C a la Asamblea.
La palabra generalmente significa compilar llevarlo desde un nivel superior
a un lenguaje de programación de bajo.
Así que este es el único paso en la recopilación de donde empiezas con un lenguaje de alto nivel
y terminan en un lenguaje de bajo nivel, y es por eso que el paso se llama compilación.
[Estudiante] Durante la compilación, digamos que usted ha hecho # include cs50.h.
¿El compilador recompilar el cs50.h, al igual que las funciones que se encuentran allí,
y traducir eso en código ensamblador, así,
o va a copiar y pegar algo que ha sido pre-Asamblea?
cs50.h bastante mucho que nunca terminan en la Asamblea.
Cosas como prototipos de las funciones y las cosas son sólo para que tengas cuidado.
Garantiza que el compilador puede comprobar las cosas como si estuvieras llamando a las funciones
con los tipos de devolución de derechos y los argumentos de derecho y esas cosas.
>> Así cs50.h se preprocesado en el archivo y, a continuación, cuando se está compilando
que es básicamente tirar después se asegura de que todo lo que se llama correctamente.
Pero las funciones definidas en la biblioteca CS50, que están separados de cs50.h,
los que no se compilan por separado.
Que en realidad se reducirá en la etapa de enlace, así que vamos a llegar a eso en un segundo.
Pero primero, ¿qué es el montaje?
[Estudiante] Asamblea a binario? Sí >>.
Maquila.
Nosotros no lo llamamos compilar porque la Asamblea es casi una pura traducción de binario.
Hay una lógica muy poco al pasar de la Asamblea a binario.
Es como buscar en una tabla, oh, tenemos esta instrucción;
que corresponde a binario 01110.
Y para que los archivos que el montaje generalmente resultados son. Archivos o.
Y los archivos. O son lo que decíamos antes,
cómo un fichero no necesita tener una función principal.
Cualquier archivo puede ser compilado a un archivo. O como mucho, ya que es un archivo válido de C.
Puede ser compilado a. O.
Ahora, la vinculación es lo que realmente trae un montón de. Archivos o y los lleva a un ejecutable.
Y así lo hace es la vinculación que se pueda imaginar la biblioteca CS50 como un archivo. O.
Es un archivo binario ya compilado.
Y así, cuando se compila el archivo, su hello.c, que llama a GetString,
hello.c se compila a hello.o,
hello.o está ahora en binario.
Utiliza GetString, por lo que tiene que ir a cs50.o,
y el enlazador ellos smooshes juntos y lo copia en el archivo GetString
y sale con un ejecutable que tiene todas las funciones que necesita.
Así cs50.o no es en realidad un archivo de O, pero está lo suficientemente cerca que no hay una diferencia fundamental.
Así que la vinculación sólo trae un montón de archivos juntos
que contienen por separado todas las funciones que necesito para usar
y crea el ejecutable que realmente funciona.
>> Y eso es también lo que decíamos antes
donde se puede tomar 1000. archivos c, se compila a todos a. archivos o,
que probablemente tomará un tiempo, y luego se cambia 1. archivo c.
Sólo es necesario recompilar que 1. Archivo c y luego todo vuelve a vincular más,
vincular todo de nuevo juntos.
[Estudiante] Cuando estamos vinculando escribimos lcs50?
Sí, así lcs50. Que las señales de las banderas al enlazador que debe ser la vinculación en esa biblioteca.
¿Preguntas?
¿Hemos ido binario excepto que 5 segundos en la primera conferencia?
No lo creo.
Usted debe saber todo de las grandes salidas que hemos repasado,
y usted debería ser capaz de hacerlo, si te damos una función,
usted debería ser capaz de decir que es grande O, más o menos. O bien, gran O es áspera.
Así que si usted ve bucles for anidados recorrer el mismo número de cosas,
como int i, i <n, int j, j > [estudiante] n al cuadrado. >> Que tiende a ser n al cuadrado.
Si ha triples anidada, que tiende a ser cortado en cubos n.
Así que ese tipo de cosa que usted debe ser capaz de señalar inmediatamente.
Usted necesita saber la ordenación por inserción y ordenación de burbuja y fusionar clase y todo eso.
Es más fácil entender por qué son los n al cuadrado y n log n y todo eso
porque creo que había un concurso en un año en el que básicamente le dio
una implementación de tipo burbuja y dijo: "¿Cuál es el tiempo de ejecución de esta función?"
Así que si usted lo reconoce como una especie de burbuja, entonces inmediatamente se puede decir n al cuadrado.
Pero si lo que se mire, usted ni siquiera necesita para darse cuenta de que es una especie de burbuja;
sólo puedo decir que está haciendo esto y esto. Este es n al cuadrado.
[Estudiante] ¿Hay ejemplos difíciles que pueden surgir con,
como una idea similar de averiguar?
>> No creo que le damos algunos ejemplos difíciles.
Lo especie de burbuja es casi tan duro como nos gustaría ir,
e incluso que, siempre y cuando usted entienda que usted está interactuando sobre la matriz
para cada elemento de la matriz, que va a ser algo que n al cuadrado.
Hay cuestiones generales, como la que tenemos aquí - Oh.
Justo el otro día, Doug afirmó: "He inventado un algoritmo que puede ordenar una matriz
"De n números en O (log n) tiempo!"
Entonces, ¿cómo sabemos que es imposible?
[Respuesta de los estudiantes inaudible] >> Si.
Por lo menos, tiene que tocar cada elemento de la matriz,
por lo que es imposible ordenar una matriz de -
Si todo está en orden sin clasificar, entonces usted va a estar en contacto con todo lo que en la matriz,
por lo que es imposible hacerlo en menos de O de n.
[Estudiante] Usted nos mostró el ejemplo de ser capaz de hacerlo en O de n
si se utiliza una gran cantidad de memoria. Sí >>.
Y eso es - no recuerdo qué eso es - ¿Está contando cosas semejantes?
Hmm. Que es un algoritmo de clasificación entero.
Yo estaba buscando el nombre especial para esto que yo no recordaba la semana pasada.
Si. Estos son los tipos de clases que pueden lograr las cosas en gran O de n.
Sin embargo, hay limitaciones, como que sólo se puede utilizar números enteros hasta un número determinado.
Además, si usted está tratando de ordenar algo Es. -
Si la matriz es 012, -12, 151, 4 millones,
entonces ese elemento solo se va a arruinar por completo la clasificación completa.
>> ¿Preguntas?
[Estudiante] Si usted tiene una función recursiva y sólo hace que las llamadas recursivas
dentro de una instrucción de retorno, que es la cola recursiva,
por lo que no habría que utilizar más memoria en tiempo de ejecución
o que por lo menos comparable utilizar la memoria como un proceso iterativo de solución?
[Bowden] Sí.
Probablemente sería un poco más lento, pero en realidad no.
Tail recursiva es bastante bueno.
Mirando de nuevo a los marcos de pila, digamos que tenemos principal
y de un bar int (int x) o algo así.
Esto no es una función recursiva perfecta, pero la barra de retorno (x - 1).
Así que, obviamente, esto es erróneo. Necesita casos base y esas cosas.
Pero la idea aquí es que esta es la cola recursiva,
lo que significa que las llamadas barra principal que va a conseguir su marco de pila.
En este marco de pila que va a ser un pequeño bloque de memoria
que corresponde a su argumento x.
Y así digamos principal pasa a llamar bar (100);
Así que x va a comenzar como 100.
Si el compilador reconoce que se trata de una función recursiva cola,
entonces cuando hace su barra llamada recursiva a la barra,
en vez de hacer un nuevo marco de pila, que es donde la pila empieza a crecer en gran medida,
finalmente se ejecutará en el montón y luego te dan segfaults
porque la memoria comienza a chocar.
>> Así que en lugar de hacer su propio marco de pila, se puede realizar,
hey, yo nunca tenga que volver a este marco de pila,
así que en vez que voy a reemplazar este argumento con 99 y luego comenzar bar por todas partes.
Y entonces lo haré otra vez y llegará bar return (x - 1),
y en lugar de hacer un nuevo marco de pila, se acaba de sustituir su actual argumento con el 98
y luego saltar de nuevo al principio de la barra.
Esas operaciones, reemplazando el valor 1 en la pila y salta de nuevo al principio,
son bastante eficientes.
Así que no sólo es el uso de la memoria misma como una función separada que se iterativo
porque sólo está utilizando un marco de pila, pero no está sufriendo los inconvenientes
de tener que llamar a las funciones.
Llamada a funciones puede ser algo caro, ya que tiene que ver todo este montaje
y el desmontaje y todas esas cosas.
Así que esta recursión de cola es bueno.
[Estudiante] ¿Por qué no crear nuevos pasos?
Debido a que se da cuenta de que no necesita.
El llamado a la barra se acaba de devolver la llamada recursiva.
Así que no hay que hacer nada con el valor de retorno.
Es sólo va a devolverlo inmediatamente.
Por lo tanto, sólo va a sustituir su propio argumento y empezar de nuevo.
Y también, si usted no tiene la versión recursiva de cola,
entonces usted consigue todos estos bares donde cuando este bar devuelve
hay que devolver su valor a ésta, luego que la barra vuelve inmediatamente
y devuelve su valor a ésta, entonces sólo va a regresar inmediatamente
y devolver su valor a ésta.
Así que usted está ahorrando esta haciendo estallar todas estas cosas de la pila
ya que el valor de retorno es sólo va a pasar todo el camino de vuelta de todos modos.
Así que ¿por qué no sustituir nuestro argumento con el argumento de actualización y empezar de nuevo?
Si la función no es la cola recursiva, si haces algo como -
[Estudiante] si bar (x + 1). Sí >>.
>> Así que si usted lo pone en condición, entonces usted está haciendo algo con el valor de retorno.
O incluso si usted acaba de hacer cambio de 2 * bar (x - 1).
Así que ahora bar (x - 1) debe volver a fin de que para el cálculo de 2 veces ese valor,
por lo que ahora necesita su propio marco de pila separada,
y ahora, no importa cuánto te esfuerces, vas a tener que -
Esta no es la cola recursiva.
[Estudiante] Lo que trato de llevar una recursividad para aspirar a una recursión de cola -
[Bowden] En un mundo ideal, pero en CS50 usted no tenga que hacerlo.
Con el fin de obtener la recursión de cola, por lo general, se configura un argumento adicional
bar donde se llevará a int x en y
e y corresponde a la última cosa que desea devolver.
Así que esto va a ser regresar bar (x - 1), 2 * y.
Así que eso es sólo un alto nivel de cómo se transforman las cosas sean cola recursiva.
Pero el argumento extra -
Y luego al final cuando llegue a su base, la que acaba de regresar y
porque ha venido acumulando durante todo el tiempo el valor de retorno que desee.
De alguna manera lo han estado haciendo iterativa, pero utilizando las llamadas recursivas.
¿Preguntas?
[Estudiante] Tal vez sobre la aritmética de punteros, como cuando el uso de cadenas. >> Claro.
Aritmética de punteros.
Cuando el uso de cadenas es fácil porque las cadenas son estrellas char,
chars son para siempre y siempre un solo byte,
por lo que la aritmética de punteros es equivalente a la aritmética normal cuando usted está tratando con cadenas.
Digamos char * s = "hola".
Así que tenemos un bloque en la memoria.
Se necesita de 6 bytes, ya que siempre se necesita el terminador nulo.
Y char * s va a señalar el comienzo de esta matriz.
Así que s apunte allí.
Ahora, esto es básicamente como cualquier matriz funciona,
independientemente de si se trataba de un retorno de malloc o si está en la pila.
Cualquier matriz es básicamente un puntero al comienzo de la matriz,
y después de cualquier operación de matriz, cualquier indexación, es sólo va en esa matriz un cierto desfase.
>> Así que cuando digo algo como s [3], lo que va a s y contar 3 caracteres pulg
Así que s [3], tenemos 0, 1, 2, 3, por lo que s [3] va a hacer referencia a este l.
[Estudiante] Y podríamos alcanzar el mismo valor que al hacer s + 3 estrellas y luego entre paréntesis?
Sí.
Esto es equivalente a * (s + 3);
y que es eterno y para siempre equivalente no importa lo que hagas.
Nunca se deberá utilizar la sintaxis soporte.
Siempre puedes usar el * (s + 3) sintaxis.
La gente tiende a gustar la sintaxis soporte, sin embargo.
[Estudiante] Así que todas las matrices son en realidad punteros.
Hay una diferencia leve cuando digo int x [4] >> [estudiante] ¿Eso cree la memoria?
[Bowden] Eso va a crear 4 enteros en la pila, por lo general de 16 bytes.
Se va a crear 16 bytes en la pila.
x no se almacena en cualquier lugar.
Es sólo un símbolo referente al inicio de la cosa.
Porque declarado la matriz dentro de esta función,
lo que el compilador va a hacer es reemplazar todas las instancias de la variable x
con el lugar donde pasó a optar por poner estos 16 bytes.
No se puede hacer eso con char * s porque s es un puntero real.
Es gratuito para apuntar luego a otras cosas.
x es una constante. No se puede tener que apunte a otra matriz. >> [Estudiante] Bueno.
Pero esta idea, esta indexación, es la misma independientemente de si se trata de un régimen tradicional
o si es un puntero a algo o si es un puntero a un array malloced.
Y, de hecho, es tan equivalente que es también la misma cosa.
En realidad, sólo se traduce lo que hay dentro de los corchetes y lo que queda de los soportes,
los suma, y ​​elimina referencias.
Así que esto es tan válido como * (s + 3) o s [3].
[Estudiante] ¿Se puede tener punteros que apuntan a 2-dimensionales?
>> Es más difícil. Tradicionalmente, no.
Una matriz de 2 dimensiones es sólo un arreglo 1-dimensional con una sintaxis conveniente
porque cuando digo int x [3] [3], esto es en realidad una matriz con 9 valores.
Y así, cuando yo índice, el compilador sabe a qué me refiero.
Si digo x [1] [2], se sabe que quiero ir a la segunda fila, por lo que va a pasar los primeros 3,
y luego quiere que la segunda cosa en que, por lo que va a conseguir éste.
Pero todavía es sólo una matriz unidimensional.
Y así, si quería asignar un puntero a la matriz,
Yo diría int * p = x;
El tipo de x es justo -
Es decir tipo rudo de x, ya que es sólo un símbolo y no es una variable real,
pero es sólo un * int.
x es sólo un puntero al comienzo de esta. >> [Estudiante] Bueno.
Así que no será capaz de acceder a [1] [2].
Creo que hay una sintaxis especial para la declaración de un puntero,
algo ridículo como int (* p [-. algo absolutamente ridículo que ni siquiera conozco.
Pero hay una sintaxis para declarar punteros como con paréntesis y las cosas.
Puede incluso no le permiten hacer eso.
Podía mirar hacia atrás en algo que me digas la verdad.
Voy a buscar más tarde, si hay una sintaxis de punto. Pero nunca lo verá.
Y aunque la sintaxis es tan arcaico que si usted lo usa, la gente se desconcierta.
Los arreglos multidimensionales son bastante raros como es.
Te prácticamente -
Bueno, si usted está haciendo las cosas de la matriz que no va a ser raro,
pero en C que está raramente va a utilizar matrices multidimensionales.
Si. >> [Estudiante] Digamos que usted tiene un arsenal muy largo.
>> Así, en la memoria virtual parece ser todos consecutivos,
como los elementos uno al lado del otro,
pero en la memoria física, ¿sería posible que se separaron? Sí >>.
¿Cómo funciona la memoria virtual es sólo separa -
La unidad de asignación es una página, que tiende a ser 4 kilobytes,
y así, cuando un proceso, dice, hey, quiero utilizar esta memoria,
el sistema operativo se va a asignar 4 kilobytes para que poco bloque de memoria.
Incluso si sólo utiliza un solo byte poco en todo el bloque de memoria,
el sistema operativo se va a dar la totalidad de 4 kilobytes.
Lo que esto significa es que podría tener - vamos a decir que este es mi stack.
Esta pila se podía separar. Mi pila podría ser megabytes y megabytes.
Mi pila podría ser enorme.
Pero la propia pila tiene que ser dividido en páginas individuales,
que si miramos hacia aquí digamos que esta es nuestra RAM,
si tengo 2 GB de RAM, es 0 dirección real como el byte cero de mi memoria RAM,
y esto es de 2 gigabytes todo el camino hasta aquí.
Así que esta página puede corresponder a este bloque por aquí.
Esta página puede corresponder a este bloque por aquí.
Esta podría corresponder a éste por aquí.
Así que el sistema operativo es libre de asignar memoria física
a cualquier página individual arbitrariamente.
Y eso significa que si esto sucede frontera a horcajadas sobre una matriz,
una matriz pasa a ser la izquierda y la derecha de este de este orden de una página,
a continuación, la matriz se va a dividir en la memoria física.
Y luego, cuando usted salga del programa, cuando el proceso termina,
estas asignaciones se borran y entonces es libre de utilizar estos bloques pequeños para otras cosas.
Más preguntas?
[Estudiante] El puntero de la aritmética. >> Oh yeah.
Las cadenas eran fáciles, pero mirando a algo como enteros,
Así que volvemos a int x [4];
Si se trata de una matriz o si se trata de un puntero a una matriz de 4 enteros malloced,
que va a ser tratado de la misma manera.
[Estudiante] Así matrices están en el montón?
Bowden [] Las matrices no están en el montón. >> [Estudiante] Oh.
>> [Bowden] Este tipo de matriz tiende a ser en la pila
a menos que lo declarado en - haciendo caso omiso de las variables globales. No utilice variables globales.
Dentro de una función digo int x [4];
Se va a crear un bloque de 4 entero en la pila para esta matriz.
Pero este malloc (4 * sizeof (int)); va a ir en el montón.
Pero después de este punto puedo usar x y p en más o menos de la misma manera,
aparte de las excepciones que dije antes acerca de usted puede reasignar p.
Técnicamente, sus tamaños son un poco diferentes, pero eso es completamente irrelevante.
Usted nunca realmente utilizan sus tamaños.
El p puedo decir p [3] = 2, o x [3] = 2;
Usted las puede utilizar en exactamente la misma manera.
Así que ahora puntero aritmética - Sí.
[Estudiante] No tienes que hacer * p si tiene los brackets?
Los soportes son un dereference implícita. >> Okay.
En realidad, también lo que está diciendo con el se puede obtener matrices multidimensionales
con punteros, lo que puedes hacer es algo como, digamos, int ** pp = malloc (sizeof (int *) * 5);
Voy a escribir todo esto en primer lugar.
Yo no quería eso.
Bien.
Lo que hice aquí es - Eso debe ser pp [i].
Así pp es un puntero a un puntero.
Estás mallocing pp para apuntar a un arreglo de 5 estrellas int.
Así, en la memoria que tienes en la pila pp
Se va a apuntar a un arreglo de 5 bloques que son ellos solos punteros.
Y luego, cuando malloc aquí abajo, yo malloc que cada uno de los punteros individuales
debería apuntar a un bloque independiente de 4 bytes en la pila.
Así que esto apunta a 4 bytes.
Y éste apunta a un diferente 4 bytes.
>> Y todos ellos apuntan a sus propias 4 bytes.
Esto me da una manera de hacer las cosas multidimensionales.
Podría decir pp [3] [4], pero ahora esto no es lo mismo como matrices multidimensionales
ya que las matrices multidimensionales traducido [3] [4] en una sola Desplazamiento en la matriz x.
Esta p desreferencias, accede al tercer índice, a continuación, elimina referencias que
y accesos - 4 sería inválido - el segundo índice.
Mientras que cuando tuvimos la int x [3] [4] antes como una matriz multidimensional
y al hacer doble soporte es realmente sólo un dereference sola
usted está siguiendo un único puntero y luego un desplazamiento,
esto es realmente referencias 2D.
Sigues 2 punteros separados.
Así que esto también técnicamente le permite tener matrices multidimensionales
donde cada matriz individual es diferentes tamaños.
Así que creo irregulares matrices multidimensionales es lo que se llama
ya que realmente la primera cosa podría apuntar a algo que tiene 10 elementos,
la segunda cosa que podría apuntar a algo que tiene 100 elementos.
[Estudiante] ¿Hay algún límite en el número de punteros que puede tener
apuntando a otros indicadores? No. >>
Usted puede tener int ***** p.
Volver a la aritmética de punteros - >> [estudiante] Oh. Sí >>.
[Estudiante] Si tengo int p *** y luego hago una desreferencia y digo * p es igual a este valor,
¿Es sólo va a hacer un nivel de eliminación de referencias? Sí >>.
Así que si quiero acceder a lo que el puntero apunta al último -
Luego de hacer p ***. >> Okay.
Así que esto es p apunta a un bloque, apunta a otro bloque, apunta a otro bloque.
Entonces, si usted hace * p = algo más, entonces va a cambiar esta
a punto ahora a un bloque diferente. >> Okay.
>> [Bowden] Y si estos se malloced, entonces usted ahora ha escapado memoria
a menos que le sucede que tiene distintas referencias de estos
ya que no puede volver a aquellos que sólo tiró.
Aritmética de punteros.
int x [4], se va a asignar una matriz de 4 enteros
donde x se va a apuntar a la comienzo de la matriz.
Así que cuando digo algo como x [1], yo quiero que signifique ir al segundo entero en la matriz,
que sería éste.
Pero, en realidad, eso es 4 bytes en la matriz ya que este entero ocupa 4 bytes.
Por lo tanto un desplazamiento de 1 significa realmente un desplazamiento de 1
veces el tamaño de cualquiera que sea el tipo de la matriz es.
Este es un arreglo de enteros, por lo que sabe hacer una veces el tamaño de int cuando se quiere compensar.
El resto de la sintaxis. Recuerda que esto es equivalente a * (x + 1);
Cuando digo puntero + 1, lo que vuelve es la dirección que el puntero está almacenando
más 1 veces el tamaño del tipo del puntero.
Así que si x = ox100, entonces x + 1 = ox104.
Y se puede abusar de esto y decir algo como char * c = (char *) x;
y ahora c va a ser la misma dirección x.
c va a ser igual a ox100,
pero c + 1 va a ser igual a ox101
ya que la aritmética de punteros depende del tipo del puntero que va a agregar a.
Así que c + 1, se ve en c, que es un puntero char, así que va a añadir una veces el tamaño de char,
que siempre va a ser 1, por lo que obtener 101,
mientras que si lo hago x, que también sigue siendo 100, x + 1 va a ser 104.
[Estudiante] ¿Se puede usar c + + para avanzar el puntero por 1?
Sí, se puede.
Usted no puede hacer eso con x porque x es sólo un símbolo, es una constante, no se puede cambiar x.
>> Pero c pasa a ser simplemente un puntero, por lo que c + + es perfectamente válido y que se incrementará en 1.
Si c fuera sólo un int *, entonces c + + sería 104.
+ + Hace aritmética de punteros como c + 1 tendría que hacer aritmética de punteros.
Esto es en realidad cómo un montón de cosas como la especie de mezcla -
En lugar de crear copias de las cosas, en su lugar puede pasar -
Al igual que si quería pasar esta mitad de la matriz - Vamos a borrar algo de esto.
Digamos que yo quería pasar a este lado de la matriz en una función.
¿Qué iba a pasar con esa función?
Si paso x, estoy pasando esta dirección.
Pero quiero pasar esta dirección en particular. Entonces, ¿qué debería pasar?
[Estudiante] Pointer + 2?
[Bowden] Entonces x + 2. Sí.
Eso va a ser esta dirección.
También muy a menudo lo ven como x [2] y luego la dirección de eso.
Así que hay que tomar la dirección de la misma debido a que el soporte es eliminar la referencia implícita.
x [2] se refiere al valor que está en esta caja de texto, a continuación, desea que la dirección de la caja,
por lo que dicen y x [2].
Así es como algo en especie de combinación en la que desea pasar la mitad de la lista a algo
lo que realmente pasa & x [2], y ahora en cuanto a la llamada recursiva se refiere,
mi nueva matriz comienza allí.
Preguntas de última hora.
[Estudiante] Si no ponemos un signo o un - ¿qué es eso llama? >> Star?
[Estudiante] Estrellas. >> Técnicamente, el operador de indirección, pero - >> [estudiante] eliminación de referencias.
>> Si no ponemos una estrella o un símbolo de unión, ¿qué pasa si acabo de decir y = x y x es un puntero?
¿Cuál es el tipo de y? >> [Estudiante] sólo voy a decir que es puntero 2.
Así que si usted acaba de decir y = x, x ahora y punto y la misma cosa. >> [Estudiante] apuntan a la misma cosa.
Y si x es un puntero int? Sería >> quejan porque no se puede asignar punteros.
[Estudiante] Bueno.
Recuerde que los punteros, a pesar de que dibujarlos como flechas,
realmente todo lo que tienda - int * x - realmente todo x es el almacenamiento es algo así como ox100,
que nos ha tocado representar como señalar al bloque almacenado a 100.
Así que cuando digo int * y = x; sólo estoy copiando en ox100 y,
que sólo vamos a representar y, también apunta a ox100.
Y si digo int i = (int) x; entonces me va a almacenar cualquiera que sea el valor de ox100 es
dentro de ella, pero ahora que va a ser interpretado como un entero en lugar de un puntero.
Pero es necesario el yeso o de lo contrario se quejará.
[Estudiante] Entonces, ¿te refieres a emitir -
¿Va a estar echando int x int o fundición de y?
[Bowden] ¿Qué?
[Estudiante] Bueno. Después de estos paréntesis se va a haber una xo ay allí?
>> [Bowden] Cualquiera. x e y son equivalentes. >> [Estudiante] Bueno.
Debido a que ambos son punteros. Sí >>.
[Estudiante] Por lo que sería almacenar el 100 en forma hexadecimal entero? >> [Bowden] Yeah.
Pero no es el valor de lo que apunta.
[Bowden] Yeah. >> [Estudiante] Tan sólo la dirección en forma de número entero. Bien.
[Bowden] Si desea por alguna extraña razón,
usted podría tratar exclusivamente con los punteros y nunca tratar con números enteros
y simplemente ser como int * x = 0.
Luego te vas a quedar muy confundido una vez aritmética de punteros comienza a suceder.
Así que los números que almacenan no tienen sentido.
Sólo es como se llega a interpretarlos.
Así que soy libre de copiar ox100 de un int * a un int,
y yo soy libre de asignar - usted está probablemente va a gritar en fundición para no -
Soy libre para asignar algo como (int *) ox1234 en este * int arbitraria.
Así ox123 es tan válida como una dirección de memoria es & y.
& Y pasa a devolver algo que es más o menos ox123.
[Estudiante] que sería una forma genial de pasar de hexadecimal a formato decimal,
como si usted tiene un puntero y lo echaron como un int?
[Bowden] Puede realmente imprimir utilizando como printf.
Digamos que tengo int y = 100.
Así printf (% d \ n - como usted ya debe saber - que imprime en forma de entero x%.
Vamos a imprimir como hexadecimal.
Así que un puntero no se almacena como hexadecimal,
y un número entero no se almacenan como decimal.
Todo se almacena en forma binaria.
Es sólo que tendemos a mostrar punteros como hexadecimal
porque pensamos en las cosas en estos bloques de 4 bytes,
y las direcciones de memoria tienden a ser familiar.
Somos como, si comienza con bf, a continuación, pasa a ser en la pila.
Así que es sólo nuestra interpretación de punteros como hexadecimal.
Bien. Las últimas preguntas?
>> Voy a estar aquí por un poco después si usted tiene cualquier otra cosa.
Y ese es el final de todo.
>> [Estudiante] Yay! [Aplauso]
>> [CS50.TV]