[MÚSICA DE FONDO]
DAVID: Muy bien.
Esto es CS50, y esta es la lección dos, nuestra continuación de C.
Y para las siguientes semanas, continuaremos usando C.
Pero nos enfocaremos menos en el lenguaje y la sintaxis, en los cuales
con el tiempo conseguiremos experiencia mediante la resolución de problemas,
y más y más sobre las ideas, y más y más sobre los problemas
que podremos resolver.
Pero antes de que sigamos adelante con algo nuevo,
echemos un vistazo rápido a dónde nos quedamos
y lo que hoy asumiremos como un nivel de comodidad
y pregunten cualquier duda que surja en el camino.
Antes, para programar necesitábamos una herramienta,
y esa herramienta era esta, CS50 IDE.
Si aún no la conocen, probablemente lo harán este fin de semana con la serie
de problemas uno.
Este será un entorno de programación basado en la web
que tiene todas las herramientas que se necesitan para
escribir un código, compilarlo y después, a partir de hoy, iniciar su depuración
o encontrar errores en él.
Pero esto es un requisito porque no es suficiente con solo escribirlo.
¿Qué nombre le damos, generalmente hablando?
Sí, es el código fuente.
Entonces, esto es código.
Cuando alguien dice, escribo un código, en realidad escriben cosas como esta.
Y este es, en particular, un lenguaje llamado C. Desde luego,
las computadoras no hablan C, tampoco hablan Java,
y no hablan Python o C++, o cualquiera de los lenguajes con los que
estén familiarizados.
Al final del día, ¿qué es lo único que entienden?
Sí, el binario.
Los números binarios son ceros y unos, que también son conocidos
como código máquina, en este contexto, en la medida que sea un código.
Son instrucciones que se implementan para resolver algunos problemas.
Pero las computadoras solo entienden ceros y unos.
Necesitábamos una herramienta para llegar desde A hasta B, ¿la cual se llama?
Sí, un compilador.
Así que un compilador, hace esto por nosotros.
El código fuente es la entrada.
El compilador es el programa, o en realidad es el algoritmo,
no obstante en la forma de una pieza de software.
Así que, la salida es el código máquina de ceros y unos.
Y para nuestros fines, no nos ocuparemos en cómo llegar del paso A al B
per se.
Queremos usar la herramienta.
Pero esta es otra área en las ciencias de la computación.
Si quieren entender cómo funcionan los compiladores,
y cómo los humanos obtenemos ceros y unos de algo llamado código
ensamblador para lenguajes de alto nivel, al que daremos un vistazo en CS50.
En sí mismo es un campo completo que al final podría resultar de interés,
pero, de manera mecánica, es así cómo compilamos el código.
Clang es un compilador,
admitido para el lenguaje C.
Es un software que algunos humanos escribieron hace años
aunque existen alternativas
si alguna vez han utilizado Visual Studio en el mundo de Windows,
o GCC en el mundo de Linux y Unix, existe un grupo de otros compiladores.
Simplemente usamos clang porque es bastante popular.
Y luego ese segundo comando es aún más extraño,
¿pero representa el acto de hacer qué?
./a.out.
Sí, ¿por aquí?
Sí, ejecuta el programa.
Exacto.
Así que ./a.out es una forma compleja de ejecutar un programa,
es el equivalente en texto de un icono de doble clic.
Y a.out es el nombre predeterminado que obtenemos
de la salida del ensamblador al compilar un programa sin nombre específico.
Pero pudimos especificar un nombre.
Si introducimos una técnica llamada argumentos de línea de comandos,
podemos ser un poco más precisos.
clang -o, para la salida de cualquier palabra que deseen.
En este caso se me ocurrió hello.
Luego el nombre del programa o el archivo que deseamos compilar.
Pero, desde luego, es bastante tedioso.
De hecho nos falta un paso.
A veces cuando queremos escribir un programa,
basta con compilar exactamente eso.
Ahora hagamos esto.
Voy a entrar al CS50 IDE y luego
brevemente haré lo siguiente.
File, New.
Luego guardaré esto como hello.c.
Y de memoria recrearé este programa rápidamente.
int main(void), y después printf ("hello, world"),
y luego, por si acaso, \ n, que significa
mueve el cursor a la siguiente línea.
Y a continuación lo guardaré.
Si ahora ejecutamos clang hello.c, vemos que luce bien.
Y ./a.out
también luce bien.
Pero recuerden que introdujimos algunas otras funciones el otro día,
como get_string y get_int,
y veremos muchas más dentro de poco.
Si hago eso, noto que tengo que añadir un par de cosas.
Así que si quiero usar algo como string name = get string
y abro y cierro comillas ("Name: "), para apuntar al usuario,
recuerden que el lado izquierdo dice, hey, computadora,
dame una variable que se almacene en la cadena
que se llame name.
Puede llamarse de cualquier forma,
pude llamarla s.
¿Por qué es mucho mejor llamar a mi variable name en vez de
string o s?
¿Sí?
PÚBLICO: Porque es más claro.
DAVID: Porque es más claro, ¿cierto?
Puede parecer bastante quisquilloso ese pequeño detalle,
pero es más claro.
Y cuando nuestros programas se vuelven más grandes, es mejor
ser capaces de leer palabras y comprender implícitamente
su significado sin tener que pensar por qué x, y, o s,
o lo que sea.
En el lado derecho, mientras tanto, tenemos esta función get_string,
cuyo propósito en la vida es obtener una cadena desde el
teclado del usuario, que apunte a una palabra como name,
y luego la devuelva, como Sam cuando me devolvió un pedazo de papel
con un nombre.
Pero aquí hay una trampa,
ya que no es suficiente solo adaptar mi código a este nuevo enfoque.
Tuve que cambiar una vez más esa segunda línea,
y tuve que dar un marcador de posición, el cual es %s.
Parece un poco enigmático, pensarlo como un Mad Lib,
si están familiarizados con ellos, donde solo hay que
llenar un espacio en blanco.
La instrucción que da %s es: pon una palabra allí.
¿Cuál palabra?
La que sea que venga después de la coma, cualquier variable o valor que esté allí.
Así que eso es bueno,
Pero hay un par de cosas que pueden salir mal
Y voy a señalarlas porque tal vez así ustedes
no se tropezarán con ellas cuando lo hagan por su cuenta.
Voy a proseguir con "clang hello.c" ahora que apliqué estos cambios
y los guardé.
De repente hay muchos errores en todo, aunque no
cambié tanto este código.
Una vez más, la regla de oro de la última vez decía,
si hay muchos mensajes de error, revisa siempre el primero
porque el resto podría ser el resultado de una cascada de errores,
de los cuales solo uno es importante, ya que es el primero.
Ahora, dice uso de cadena identificadora no declarada.
¿Quisiste decir stdin?, lo cual es algo completamente diferente.
No lo hice.
Quise decir "string".
Resulta que la cadena es una función de la llamada biblioteca CS50.
Esto es una rueda de entrenamiento que usaremos durante algunas semanas
hasta que nos adentremos también en el mundo de las cadenas.
Pero, para usar cualquier cosa de CS50, ¿qué necesito agregar también a mi código?
PÚBLICO: El código fuente.
DAVID: El código fuente, sí.
Pero, ¿qué más?
PÚBLICO: La biblioteca.
DAVID: La biblioteca.
Y la biblioteca fue la biblioteca CS50.
Eso significa que hay un archivo en algún lugar de la computadora, en el IDE,
llamado cs50.h, conocido como archivo de encabezado, ya veremos más sobre eso.
Entonces, me olvidé de ese detalle.
Supongamos que ustedes tampoco recuerdan eso,
Bueno, quizás recuerden, de una de nuestras sesiones de orientación,
que CS50 también tiene herramientas que los ayudarán con esto.
No tienen que recurrir a este foro de discusión en línea del curso.
No necesariamente tienen que trabajar horas extra
por mensajes de error como este.
Si no tienen idea de lo que está sucediendo,
hagan lo siguiente.
En lugar de solo ejecutar clang hello.c, pondremos algo
como help50 clang hello.c, donde help50 es
un comando específico del CS50, una especie de ayudante virtual.
Si reconocemos nuestros mensajes de error, en amarillo en la parte inferior,
resaltaremos el primero.
Luego les daremos consejos de cómo pueden obtenerlos.
Entonces, en" identificador no declarado" clang identificó
que usamos un name, string, en la línea 5
de hello.c, que no fueron definidos.
¿Olvidamos incluir cs50.h, en donde la cadena de nuestro
archivo está definida?
Generalmente intentaremos inducir preguntas retóricas que,
con suerte, nos conducirán hacia la solución correcta.
Muy bien, al menos eso refrescó mi memoria, pero aún no estoy 100 %
cómodo con lo que hacen estas líneas.
Y hoy definiremos un poco más todo esto,
siento que estoy tratando de hacerlo,
pero resulta que hay otro, aquí lo tengo, clang hello.c, Enter.
Diablos.
Bueno, de hecho esto es un positivo neto, ¿cierto?
Parece que hay menos mensajes de error,
pero ahora hay uno que no vimos el otro día...
undefined reference to 'get_string'
Así que es muy parecido,
pero algo no se entiende.
Sin duda es una redacción diferente
y no estoy muy seguro de lo que significa.
Pero resulta que no basta
con usar la mayoría de las bibliotecas que incluyen a cs50.h u otro archivo
de encabezado, como finalmente veremos.
Eso solo le enseña al compilador que algo existe.
Eso como la última vez, cuando hablamos de prototipos,
donde pusimos una pequeña frase que solo decía, por cierto,
esta función existirá en algún momento.
Eso es lo que hace el archivo de encabezado.
Es como una promesa a clang de que esto existirá.
Pero existe un segundo paso.
Tenemos que alimentar a clang de los ceros y unos que
implementa la biblioteca CS50 que, desde luego, nosotros mismos no creamos
pero que preinstalamos en el IDE.
Existe una manera de hacerlo por separado,
en lugar de solo escribir clang hello.c, tenemos que
hacer lo que se denomina vincular nuestro código con esa biblioteca,
al menos si es una biblioteca de terceros que no viene con la computadora,
vino de humanos como nosotros.
Entonces ahora esto dice, hey clang, compila hello.c, pero vincúlalo
con la biblioteca CS50, eso significa que tomo mis ceros y unos,
tome los ceros y unos de CS50, los combine y luego me de
mi programa para ejecutarlo.
Eso será un ingrediente clave
y help50 podría guiarnos hacia esa solución.
Si ahora presiono Enter, parece que compila.
./a.out.
Mi nombre debe ser David.
Enter.
Y dice, "hello, David".
Nuevamente, no se obsesionen ni trabajen horas extra para arreglar
este tipo de errores.
Así sortearán este tipo de obstáculos desde el primer momento.
Pero fíjense, busquen palabras familiares,
usen cosas como help50,
accedan al curso en línea
y superen estos obstáculos porque, a fin de cuentas,
lo más interesante será la lógica
de los programas y los problemas que tratamos de resolver.
Entonces, ¿qué está ocurriendo por debajo del agua?
Y francamente, esto se está volviendo tedioso.
Entonces, ¿cómo automatizo algunos de estos procesos?
Porque esto es muy fácil de olvidar y es muy aburrido
seguir escribiendo tantos comandos.
Bueno, recuerden que había un atajo del que
hablé el otro día, en donde se esconden todos estos detalles.
No necesitamos recordar -o.
No necesitamos recordar -lcs50.
¿Qué hacemos en vez de usar "make" en un programa?
¿Sí?
Sí, así que háganlo, eso no es un compilador en sí mismo,
es un tipo de programa que nos ayuda a ejecutar un compilador.
Así que la estrategia más sencilla es poner, make hello.
Y la razón por la que produce muchas más palabras es porque lo tenemos,
anticipándonos a las enseñanzas del semestre, lo tenemos
preconfigurado con líneas de comandos argumentos, palabras adicionales,
que esperamos necesitar en algún momento.
Esto nos ahorra la molestia de andar dándole vueltas al manual
para resolver ese tipo de cosas.
Esa es la razón por la que parece complejo,
pero noten que esta es la palabra más importante,
hello seguido de hello.c,
esos son sus dos ingredientes.
Muy bien.
Entonces, ¿qué está pasando debajo del agua?
Resulta que, aunque podemos simplificar la estructura de comando,
en realidad hace bastante por nosotros.
Y es el proceso de compilación.
Pero fue una especie de simplificación excesiva, o asignarle más
inteligencia, como una abstracción.
De hecho tenemos algunos pasos bajo la manga, por ejemplo,
el procesamiento previo, la compilación, el ensamblaje y la vinculación.
Así que, adentrémonos rápido,
pero vamos a abstraernos para ver lo que está pasando.
De ahora en adelante, podemos dar por hecho que todo esto está sucediendo.
Así que aquí está el código fuente, el mismo programa con la versión
más sencilla que teníamos hace un momento.
Y finalmente, necesito obtener esto para el código máquina.
Veamos si podemos visualizar cómo llegamos del punto A al B
sin abstraerlo por completo con solo esas grandes flechas.
Este es mi código fuente.
Y resulta que el primer paso
de convertir el código fuente en código de máquina en el mundo de C,
lo ejecutar primero lo que llamamos preprocesador.
No haremos esto explícitamente, aunque
podríamos si tuviéramos un nivel bajo y estuviéramos interesados.
Esencialmente, lo que el preprocesador hace
es que al comenzar cualquier línea de código lo hace con un signo de libra,
o un hashtag actualmente, el cual es un comando especial que será
reemplazado con el contenido del archivo, al menos en este caso.
En algún sitio hay un archivo llamado stdio.h.
Entonces, #include significa obtener ese archivo, copiarlo y pegarlo ahí.
Así, cuando preprocesamos nuestro código, esta línea amarilla
se convierte en algo como esto.
Y pongo "..."
Tiene docenas, si no es que cientos, de líneas de largo.
Pero hay una línea especial que es la pequeña pista de
clang de que printf existe.
Y es por eso que necesitamos stdio.h
para nuestros propósitos de todo lo que hace el preprocesador.
Hace este tipo de hallazgo y reemplaza operaciones de estilo
para que ahora nuestro archivo, sin que lo supiéramos,
de repente se hiciera mucho más grande, porque se añadieron otras líneas de código
que alguien más escribió.
Y nuestro código permanece como era antes.
El siguiente paso después de preprocesar es la denominada autocompilación,
en la que técnicamente el compilador, si queremos
ser quisquillosos y lo vemos en su definición formal,
al tomar estas líneas amarillas, nuestro código fuente, y quizás el de
alguien más se convierte en algo llamado lenguaje ensamblador.
Este es un tipo de lenguaje que los seres humanos todavía utilizan,
pero de vuelta al tema, programaban con él.
Si tienen una computadora con un CPU Intel, un cerebro hecho por Intel,
dentro de su computadora había, y aún hay, un gran manual de usuario
que le dice a los programadores de todo el mundo que este CPU Intel entiende
las siguientes instrucciones: sumar, restar, multiplicar, dividir,
todo lo básico, y luego cosas como mover números de aquí a acá,
leer los números desde aquí hasta aquí, solo cambian las cosas de sitio
en la memoria de la computadora.
Y así, aun cuando esto parece misterioso, incluso
para mí, ya que no soy ningún experto en lenguaje ensamblador,
sin duda, todos estos años después, aún podemos ver palabras con un sonido
familiar, como "mov" que sugiere mover un valor de una ubicación a otra,
sub se refiere a la sustracción, es decir, restar un número a otro.
Y si no se piensa en esto cuidadosamente,
aún no estoy muy seguro de lo que pasará.
Pero veo una palabra familiar allá abajo llamada printf.
Esto se resume en que una computadora o compilador específico
tomó a mi más amigable código C y lo convirtió
en algo un poco más cercano a lo que una máquina entiende.
Pero eso no es todo, porque las máquinas solo
entienden ceros y unos.
Así que hay otro paso llamado ensamblado.
Y el proceso de ensamblado toma el lenguaje ensamblador
y lo convierte en ceros y unos.
Ahora se redujeron a ceros y unos.
Y lo que es increíble, si desde el principio es interesante,
cuando se ejecuta clang y se presiona Enter, todo esto sucede al instante.
y obtenemos estos ceros y unos, esta salida.
Pero dejé espacio del otro lado, porque todo lo que hicimos
fue convertir mi código, del código fuente al código ensamblador, al código máquina.
¿Qué necesitamos fusionar, por así decirlo, para ese programa "Hello, world"?
Sí, aún necesitamos algo como stdio, la biblioteca I/O estándar que tiene a printf.
El siguiente paso es tomar muchos ceros y unos
desde otro lugar en el sistema y combinarlos
hasta que este sea el archivo que contenga a a.out o hello,
como sea que hayamos nombrado al programa.
Eso, en definitiva, es lo que la computadora entiende.
Es un detalle de bajo nivel.
Afortunadamente, aprendimos en la primera lección
esta noción de abstracción, es decir, incluso mientras nos adentramos
y entendemos cómo se construye.
A partir de ahora, cada minuto en lo sucesivo, todo ese trabalenguas
se convierte en compilación.
Eso es lo que la mayoría de las personas en el negocio de la programación
conoce como compilación, son todos esos pasos.
Pero eso es todo lo que sucede.
Se siente mágico, pero paso a paso
para acercamos a nuestro objetivo.
¿Preguntas?
Se trata del nivel más bajo que veremos.
¿Sí?
PÚBLICO: ¿Por qué se tiene que pasar del lenguaje ensamblador al
código máquina?
¿Por qué no solo vamos directo al código máquina?
DAVID: Buena pregunta.
¿Por qué tenemos que ir paso a paso, desde el código fuente pasando por el lenguaje
ensamblador y al final el código máquina?
Nosotros definitivamente podríamos.
Simplemente sucede que hay muchos humanos en el mundo
y mucha gente que trabaja en diferentes proyectos.
Y esta noción de poner capas en el software
por encima del otra persona, y del de los demás, nos permite
construir sistemas más complejos mucho más limpios, por así decirlo.
Existen diferentes tipos de computadoras en el mundo.
Hay Macs,
Hay PCs que incluso en estos días,
son mucho más parecidas internamente
que las que existían en aquellos tiempos, son diferentes CPU.
Hay teléfonos que cuentan con CPU muy diferentes.
¿No sería genial si pudiera escribir mis programas en un lenguaje
y compilarlos en ceros y unos que funcionaran en una Mac, en una PC,
en un teléfono Android, en un iPhone y en otros dispositivos?
Y es por eso que al tener estos tipo de capas diferentes,
una persona o un grupo pueden implementar el proceso de conversión de C
al lenguaje ensamblador
y alguien más puede tomar los ceros y unos, en cierto sentido.
O incluso, pueden existir pasos intermedios.
Los compiladores cuentan con front ends, back ends y toda esta complejidad.
Pero nos da ventajas porque eso significa que
podemos decidir cuáles son los tipos de hardware que los soportan más fácilmente.
Muy buena pregunta.
¿Alguna otra pregunta?
Ok.
Después de esto, consideraremos cualquier número
de maneras en que las cosas pueden salir mal.
Sin duda para mí es sencillo escribir hello, world,
y hacer funcionar todo esto.
E incluso cuando no sea así, sé cómo solucionarlo rápidamente.
Esto proviene de la experiencia y la práctica.
Proseguiré con un adelanto de help50 y de otras dos herramientas
que veremos para las series de problemas, y que no necesariamente
les enseñaran a escribir un buen código, un código bueno y eficiente.
Ahí es donde entran humanos, compañeros, retroalimentaciones, secciones
horas de trabajo extra y más,
pero al menos escribiremos correctamente un código según nuestras especificaciones,
que esté bien diseñado y que al menos se vea bien.
Recuerden que el tercer ingrediente, además de la veracidad y el estilo,
será el diseño, que es algo que
aprenderemos con la práctica y los ejemplos.
Recuerden que Check50 es una herramienta que está en
CS50 IDE, sé que no están familiarizados y es lo que me permite hacer esto.
Dejen reduzco esto para volver a mi programa
programa similar a hello, world.
Ya no necesito la biblioteca CS50.
Puedo ejecutar make hello.
Parece que funciona.
¿Cómo van a comprobar sus programas si escriben
esto para una serie de problemas?
Bueno, la forma más fácil y directa, por supuesto,
es ejecutarlo.
Parece que es correcto,
y lo es.
No hay mucho que pueda salir mal en este programa.
Pronto verán, en la serie de problemas uno
y en las siguientes, que siempre que comiencen a recibir información
de un usuario, este tiene que escribir su nombre o un
número u otras cosas con los que podamos inventar escenarios
cuando algo salga mal.
Si ejecutamos un comando, en este caso check50, podremos hacer lo siguiente.
Primero crearé un directorio llamado...
A continuación voy a escribir mkdir
para crear el directorio, hello.
No vimos esto el otro día,
pero lo veremos en las excelentes secciones de hoy,
o en las secciones de todas las clases, que también serán filmadas.
Solo moveré este archivo a un directorio llamado hello.
Eso es como en una Mac o PC, simplemente arrastran y sueltan,
pero esta vez lo hago con mi teclado.
¿Cuál es el comando para cambiar a otro directorio? cd.
Es como hacer doble clic en un directorio,
aunque desde mi teclado.
A continuación ejecutaré esto.
Puedo ejecutar de nuevo make hello
y parece que funciona.
Y puedo ejecutar ./hello,
y parece que funciona.
Ahora veamos si CS50 está de acuerdo,
así que escribo check50,
después escribiré cs50/2017/fall/hello,
lo cual parecen muchas carpetas, pero no lo son,
es solo un identificador único que tiene algún tipo de jerarquía.
Solo sabríamos escribirlo al leer la serie de problemas
que se especifican en línea,
Y lo que esto hará, si no lo han visto antes,
es conectarse al servidor de CS50.
Nos autenticará, si aún no lo han hecho.
Luego iniciaré sesión como student50
y de ahora en adelante recordará mi contraseña,
al menos por un tiempo no tendremos
que escribirla cada vez que entremos.
Se está preparando,
está cargando.
Y lo que sucede ahora es que mi archivo hello.c,
está en algún lugar de la nube, en los servidores de CS50.
Ejecutamos los checks, las pruebas que el personal escribió.
Y con suerte veré muchas caras verdes sonrientes
que lucen un poco amarillas en este proyector,
pero que son, de hecho, caras verdes sonrientes
en vez de caras con el ceño fruncido, que sugerirían que algo está mal,
por lo tanto está bien.
No se desanimen si ven algunas caras con el ceño fruncido,
o algunas caras sin expresión o confusas que denotan que algo más está mal.
Pero style50 hace algo diferente,
en este momento, el estilo de mi código luce
bastante bien porque es difícil equivocarse cuando es algo tan corto.
Pero ya lo veremos.
Y si ejecuto style50 hello.c, que es el nombre del archivo que deseo
verificar,
se ve bien, pero veremos si agrega más comentarios.
Es bastante convincente porque no hay en este momento,
entonces, ¿qué tipo de comentarios les gustaría agregar?
Bueno, en este programa no es obligatorio agregar muchos comentarios
porque la realidad es que el programa es tan corto que probablemente me lleve
menos tiempo leer el código que los comentarios.
Pero es muy común, como verán en los ejemplos
de la lección, hacer algo como esto,
"Di hola al usuario", es solo un resumen rápido de una línea para que
cuando echen un vistazo al archivo o miren el código digan, Ok, lo tengo,
sé lo que hace esto.
Y si me interesa saber cómo funciona, entonces puedo leer el código.
Eso sería un comentario.
Y eso probablemente hará feliz, en este caso, a style50.
Pero ¿y si me estoy volviendo un poco descuidado?
Recuerdo vagamente que tenía el hábito de golpear la Tab o la tecla espaciadora
en la lección.
Pero no me molestaba en hacerlo cuando trabajaba en mis series de problemas.
Solo quería que esa cosa funcionara.
No es raro que el código comience a verse así,
a pesar de que el programa es muy sencillo.
El buen estilo, como lo verán y lo aprenderán en la práctica,
dicta que solo en Scratch dónde había esas piezas de rompecabezas
amarillas que abrazaban al código como si fueran llaves,
deberían verse sangrías.
A continuación, olvidaré eso y ejecutaré style50 en hello.c,
veré que mi código envía la salida a la ventana de
terminal, en la parte inferior de la pantalla.
El verde sugiere hey, programador, agrega los siguientes caracteres,
entonces, el verde sugiere agregarlos aquí.
Ahora pondré una sangría golpeando la tecla
tab, específicamente cuatro espacios, lo cual es una convención humana,
que de nuevo debería hacerlo feliz.
Sin embargo podemos ir en la dirección opuesta.
supongamos que estoy un poco confundido en cuanto a lo que
debería poner sangría, e incluso podrían consultarlo
en los libros de texto y en algunos recursos en línea,
algunas personas escriben su código así.
Ahora, ejecutaré style50 en esto
y se imprimirá mi código,
y en este caso, el color rojo indica que eliminemos esos caracteres
que de otra manera podríamos no ver.
Por lo tanto, no siempre será perfecto.
Especialmente cuando los programas son largos,
podría no ser tan obvio notar qué cambios se tienen que hacer.
Pero al igual que con los mensajes de error comiencen en la parte superior.
Hagan uno o pocos cambios,
guárdenlos, vuelvan a ejecutarlos, y vean cuál es el consejo actualizado.
No puedo enfatizar esto lo suficiente, en especial con la serie de problemas uno
y en cualquier serie de problemas posteriores, no adquieran
el hábito de sentarse y tratar de
abarcar la totalidad un problema.
Posiblemente con Scratch no nos sentamos y escribimos algo completamente
sin jugar con él una vez, probarlo o agregarle funciones.
Entonces, no adquieran ese hábito en C, y vayamos paso a paso
como lo hemos hecho hasta ahora con los ejemplos.
Muy bien.
¿Alguna pregunta sobre esas herramientas?
En un momento regresaremos con unas técnicas de depuración
más sofisticadas.
Bien.
Uno de los problemas con los que nos distrajimos anteriormente
son los juegos antiguos como Súper Mario Bros.
en donde un personaje como este salta bastante por la pantalla.
Es uno de los primeros juegos de Nintendo, en donde
existen muchos obstáculos en el camino de Mario, mientras corre de un lado a otro
y salta.
Algunos de estos obstáculos pueden ser representados
con estructuras bastante sencillas, como ladrillos, en un mundo colorido.
Y podemos acercarnos a esto al usar caracteres en nuestras
pantallas.
De hecho estuve curioseando demasiado tiempo anoche
viendo los viejos mapas de Súper Mario Bros., como si los tuviera
en la década de 1980, lo que habría hecho mucho más fácil jugar Súper Mario Bros.
La gente ha capturado todas las imágenes de este juego.
Y una imagen de este juego era una pantalla como esta.
Se supone que al final Mario debe cruzar por aquí.
Se supone que debe chocar con su cabeza los signos de interrogación,
obtener monedas y cosas por el estilo.
Por ahora voy a simplificar realmente esto y mi propósito
es cuidar el buen camino de la discusión
y concentrarme en los signos de interrogación.
¿Cómo haría en un programa de computadora, ya sea Súper Mario Bros.
o aquí en Sanders, para que imprimiera una línea de signos de interrogación
en una fila como esta?
Bueno, ahora abriré CS50 IDE,
luego crearé aquí un nuevo archivo
y después llamaré a esto, digamos, mario0.c
porque es el primero o la versión cero de este programa.
Y solo quiero imprimir cuatro signos de interrogación.
Probaré primero esto.
#include stdio.h, ¿cuál es el creo que necesito?
PÚBLICO: ¿Printf?
DAVID: Sí, necesito printf.
Necesito ser capaz de imprimir el carácter.
Entonces, int main(void) es lo que sigue.
Desde hoy por esa razón comenzaremos a separar.
Y ahora voy a imprimir ????
después punto y coma.
Muy bien.
Ahora ejecutaré make mario0.
./mario0.
Tengo una especie de representación textual muy fea
de un juego que, al menos al estilo de los ochenta, era divertido.
Hay un pequeño error estético,
y cometí este mismo error el otro día.
¿Cómo muevo mi cursor a la siguiente línea?
Sí, con \ n.
La diagonal invertida es la que vamos a escribir, y la diagonal derecha o diagonal
es lo que la gente llamaría la otra dirección.
Esta es una \ n,
y ese es un carácter especial de escape, por así decirlo.
Por ahora, solo sé que esto comienza a confundir a la computadora,
si simplemente presionamos Enter.
Ahora, nuestro código está en dos líneas, cuando en realidad es
solo una idea o una función.
Los humanos decidieron hace un tiempo que representarían ese carácter especial
que ahora ustedes solo presionan en el teclado.
Ahora, si vuelvo a ejecutar make mario0.
./mario0.
Ok, ahora luce un poco mejor.
Sabemos desde el inicio que no solo necesitamos más de un signo de interrogación,
interrogación, interrogación, interrogación,
especialmente si quiero tener aún más monedas disponibles en la pantalla.
¿Cuál es la construcción de programación correcta que me da más de esto?
PÚBLICO: Un bucle for.
DAVID: Sí, algo como un bucle for, ¿verdad?
A continuación ajustaré esto un poco.
Y luego pondré, digamos, mario1.c,
entonces, en vez de mario1.c, pondré lo siguiente
Así que, for int, para que me dé un entero,
i = 0; de manera predeterminada, aunque podría ser 1,
pero los programadores tienden a usar 0,
i menor que...
no estoy seguro, así que pongamos una gran línea en blanco por un momento,
y luego i++, porque recuerdo que es la manera de sumar.
Después, dentro de este bucle, pondré printf ("?")
punto y coma.
Ahora respondamos esta pregunta,
si este bucle, el cual tiene un proceso muy metódico,
inicializa, verifica la condición, hace algo,
incrementa, verifica la condición, repite una y otra vez.
¿Qué número debo poner sobre la línea en blanco?
PÚBLICO: Cuatro.
DAVID: Entonces cuatro.
Pero si estoy contando de 0 a 4, parece como si fueran cinco números.
Así que, tres podrían quedarme más cerca, pero hay un menor que.
Tenemos este operador relacional,
menor que, pudo ser mayor que en otros contextos.
Menor que en realidad nos salva.
Si tengo for aquí, piensen lógicamente lo que está sucediendo.
Inicializamos en 0 por primera vez.
Obtenemos un signo de interrogación impreso.
Entonces obtiene ++, y se convierte en 1, que es menor que 4.
Esa es la primera vez que imprimí un signo de interrogación.
Entonces i se convierte en 1, después
imprimo otro,
i ahora es 1.
Añado otro
i ahora es 2.
Añado otro,
i ahora es tres, lo cual no es consistente con la cantidad de dedos
Lo sostengo porque comencé en 0.
Una vez que i llega a 3, y por lo tanto ya imprimí mis cuatro interrogaciones,
el siguiente valor que i tomará es el mismo 4.
¿4 es menor que 4?
No, entonces ya no puedo imprimir otro signo de interrogación,
ni de levantar otro dedo.
Sinceramente, es un desperdicio de capacidad intelectual
pensar, Ok, ¿cuántos números están entre 0 y 4?
Podríamos tener, como la mayoría de los que están aquí piensa,
podríamos hacer que i sea menor o igual a 4,
y habría funcionado.
Tal vez esto es aún más claro,
porque comenzamos en 1 y contamos hasta el número 4.
Y eso también me dará cuatro dedos.
¿Por qué empezamos a contar en 0?
Es solo porque, técnicamente, es más sencillo
comenzar a contar con todos los bits en 0, desde nuestra primera lección.
Así que, es solo un hábito.
Y está bien, si se sienten más cómodos de esta manera.
En poco tiempo, esto se vuelve un hábito
porque todos los demás lo hacen de esta manera.
OK, a continuación imprimiré esto.
make mario1.
./mario1.
Ah, todavía está ese error.
OK, ¿esto lo arreglará?
¿Por qué no?
Sí, eso pondrá interrogación, nueva línea, interrogación, nueva línea.
No está bien.
¿En cuál número de línea debería ir realmente la \ n?
PÚBLICO: Debería ir fuera del bucle for.
DAVID: OK, entonces debería ir fuera del bucle for.
Vi una mano atrás en específico,
¿En cuál número de línea?
Sí.
PÚBLICO: ¿Ocho y nueve?
DAVID: Sí, entre la ocho y la nueve.
En este momento no hay espacio,
pero no hay problema.
Presionaremos Enter, printf.
Y puedo, ciertamente, poner una simple \ n,
incluso sin poner palabras a la izquierda.
Esto está bien.
Voy a recompilar esto,
si les aburre escribir de nuevo los mismos comandos,
también pueden usar su teclado para subir y bajar
y esto les mostrará su historial, por así decirlo.
Y con el paso de los días comenzarán a ahorrar tiempo.
Así que tenemos make mario1.
Enter.
./mario1, o me desplazo hacia atrás, como lo hice antes.
Ahora obtengo esos cuatro signos de interrogación.
Haré esto un poco más interesante, de la siguiente manera.
Ahora continuaré solamente y no solo aplicaré el hardcode 4 en este programa.
Seguiré con una versión más de Mario, a la que llamé mario2.c,
que esta vez obtiene alguna entrada del usuario.
Qué tal si pongo int n, porque es como un número
y es una convención bastante común llamarlo así.
y luego podemos poner get_int("Number: ");
y luego, en vez de aplicar un hardcoding 4, ¿por qué no pongo n?
lo cual puedo hacer, sin duda alguna.
Ahora ejecutaré make mario2.
Oh, oh.
Error.
Sí, se me olvidó cs50.h.
Tengo que volver arriba.
Solo haré una copia rápida, pegaré, y luego cambiaré la palabra cs50.h.
Ahora limpiaré mi pantalla.
Y para limpiarla, podemos presionar Control+l,
por ejemplo, eso dejará menos caracteres en nuestra pantalla.
make mario2.
Funcionó.
No necesité preocuparme por -lcs50 porque, una vez más, make hace eso por mí,
es una de sus características.
Y ahora puedo ejecutar make mario2.
Number:
¿Cuántos signos de interrogación queremos?
PÚBLICO: Siete.
DAVID: Escuché primero siete
y ahora tenemos siete signos de interrogación.
Y no necesariamente se va a ver súper bonito.
Si pongo 700, obtendré muchísimos.
Pero miren lo rápido que lo hizo para mí.
Ahora tenemos este poder de los bucles.
Eso está bien pero, ¿saben qué?
Veamos, ¿qué pasa con esto?
¿Qué pasa si es negativo y hay -50 signos de interrogación?
¿El 50 negativo es un int?
PÚBLICO: Sí.
DAVID: Sí, lo es.
entonces, los obtendremos por medio de la función get_int,
realmente no es lógico lo que queremos,
así que piensen en ello.
En la línea 7, si n es igual a -50, ¿cuántas veces se ejecutará el bucle?
PÚBLICO: Ninguna.
DAVID: ¿Por qué ninguna?
PÚBLICO: Porque el 0 es mayor.
DAVID: Sí, porque en este caso 0 es mayor que -50.
Entonces esa condición nunca permitirá que el bucle se ejecute lógicamente.
Entonces, Ok,
Parece que nada ocurre,
solo obtengo esta horrible línea en blanco.
Posiblemente sea un error,
pero al menos no congeló la computadora e imprimió cosas
un infinito número de veces, como en realidad podría pasar.
Ahora voy a arriesgarme
a perder el control de mi computadora y cambiaremos la lógica.
Supongamos que cambio el menor que por un mayor que,
e iniciamos n en -50.
Ahora, ¿0 es mayor que -50?
PÚBLICO: Sí.
DAVID: Sí.
Y lo más probable es que sea así por mucho tiempo,
incluso si lo incrementamos.
Ahora ejecutaré make mario2 y aguantaré la respiración,
pongo -50.
Pero incluso las computadoras y el Internet no se actualizan constantemente.
Esa es la razón de que veamos una ráfaga como esta.
Estamos enviando miles, decenas de miles, y a la larga, millones
de signos de interrogación a través de la pantalla.
Y esto también podría pasarnos por error.
Y como yo, pueden usar la combinación secreta que casi siempre funciona,
la cual es Control-C para cancelar.
Eso detendrá la ejecución de un programa en la ventana.
Muy bien.
De modo que me he adelantado e implementé
una aproximación muy débil de esto.
Lo cual es genial.
Ahora, demos un paso adelante y no solo examinemos este constructo,
más bien avancemos rápidamente en el juego, a esta parte de la pantalla,
ahora tal vez tengamos también un bloque vertical.
Y pensemos por un momento, ¿qué pasa si necesito modificar
mi código? ¿si deseo que ponga tres o cualquier número de bloques verticales?
Básicamente, ¿cómo quiero que cambie el código?
¿Cómo quiero que cambie el código?
Sí.
PÚBLICO: [INAUDIBLE]
DAVID: Sí, solo necesito un salto de línea donde por error casi lo puse
antes.
Pero en este caso, sería algo bueno.
Después ejecutaré rápidamente make mario3
comenzando desde el mismo punto.
Entonces, mario3.c.
Luego iré aquí y cambiaré esto de la siguiente manera.
Hagamos que i sea menor que n, como se supone que debe ser,
de modo que no lo olvidemos, si lo cargamos más tarde.
Y ahora pondré solo un #, porque se parece más a un ladrillo.
No es una de esas cosas con monedas.
Y luego hago esto aquí.
Creo que ya no necesitaré esto.
Ahora lo guardaré y ejecutaré make mario3
Parece estar bien y que se compila sabiamente, ./mario3, Number: 3
Y consigo tres de esos.
Ahora tal vez podría hacer que fueran cinco.
Eso funciona y así sucesivamente.
Todavía hay una oportunidad para mejorarlo,
en caso de que quiera molestar al usuario para que coopere,
de modo que si escribe en -50, no quiero que simplemente renuncie.
Quiero gritarle, o de alguna manera darle retroalimentación,
y decirle, dame lo que te pedí.
¿Cómo molesto a un usuario una y otra
y otra vez hasta que me dé el valor que quiero?
¿Perdón?
PÚBLICO: Con while.
DAVID: Con while.
Entonces hay diferentes construcciones de bucle
como while, y parece que podríamos usar while, incluso for
o también existe otro.
Analicemos cómo puede hacerse.
Resulta que si queremos obtener la entrada de un usuario, de alguien,
podemos utilizar for
o también while.
Pero descubriremos que es un poco fastidioso usar esas construcciones.
Primero veremos la mejor manera y luego veremos otra.
Si queremos que un programa haga algo al menos una vez,
o tal vez más veces, podríamos utilizar for o while.
Pero en realidad es un poco más sencillo si ponemos
while (_______); algo sea cierto.
Ahora, esto solo es solo un marcador de posición.
Ahora comenzaré a llenarlo con cierta lógica.
Entonces quiero decir, haz lo siguiente mientras ¿qué?
Si el usuario no me da un número positivo,
quiero inducirlo nuevamente.
Por el momento las llaves en las líneas siete y nueve indican exactamente eso.
Haz esto, haz esto, haz esto, mientras la línea 10 sea verdadera.
¿cuál expresión booleana, por así decirlo, escribiría en los paréntesis
de la línea 10 para pedir que siga haciendo lo mismo
hasta que el número sea positivo?
¿Sí?
PÚBLICO: Mientras n sea mayor que 0.
DAVID: Eso sería: while (n > 0), sigue haciendo...
¿Cuál?
PÚBLICO: Menor que.
DAVID: Escuché menor que.
OK, vamos a reconsiderarlo.
While (n < 0); pídele un número al usuario,
pídele un número al usuario,
pídele un número al usuario.
Pero ¿saben algo?
Esto solo va a enredarlo todo.
Seamos aún más claros en nuestra petición.
Dame un número positivo,
y sigue pidiéndoselo hasta que obtengamos un número positivo.
Ahora, si nos ponemos realmente quisquillosos, tampoco es menor que.
Estamos muy cerca.
PÚBLICO: Menor o igual.
DAVID: Es menor o igual.
Desafortunadamente, no recuerdo que mi teclado tenga esa tecla,
algo como un paréntesis en ángulo y una línea debajo de él,
como el que escribía en la clase de matemáticas.
Pero hay una forma de hacerlo en nuestro teclado.
De hecho, los escribo uno al lado del otro, <=-, así.
Esto, (n <= 0), es menor o igual a,
Esto, (n >= 0), sería igual o mayor a.
Ahora nos sentiremos cómodos leyendo esto de izquierda a derecha,
no hay un símbolo especial, como los que hay en un libro de matemáticas
o en una tarea impresa.
Entonces, creo que esto dice lo correcto,
Haz esto mientras n sea menor o igual a cero, lo cual desde luego, no es positivo.
Y luego, abajo, el resto de mi código puede permanecer igual.
Ahora solo tengo un bloque de código que está haciendo algo,
pero saben, ¿qué?
Aquí es donde los comentarios comienzan a ser útiles.
Pide al usuario un número positivo.
Aquí abajo, imprime tantos bloques.
Es muy claro, si leemos en el código lo que acabo de decir.
Pero esto nos ayuda si nos dormimos y al despertar
queremos recordar, ¿por qué hice esto?
¿Por qué hice aquello?
También le ayuda al lector de nuestro código, un colega, un compañero de clase,
etcétera.
De este modo, le agregamos comentarios al código.
Lamentablemente, hay un error y estamos a punto de llegar a él.
Déjenme intentarlo.
A continuación, ejecutaré make mario3.
Oh, ¡Dios mío!
Parece que tengo más errores, que líneas de código,
esto es extraño.
Error, variable n no utilizada.
Ahora, profundizaré en estos mensajes de error
para que comencemos a notar pequeñas pistas.
Aquí a la izquierda está, por supuesto, el nombre del archivo,
como habrán notado es mario3.c.
Hay dos puntos, un número, dos puntos y otro número: mario3.c:9:13.
Esta es una forma muy breve, para decir que en mario3.c
en la línea nueve, del carácter o columna, de izquierda a derecha, 13,
tenemos un problema, o al menos eso piensa el compilador.
En general, el carácter es bastante útil.
El número de línea atrae nuestra atención al lugar correcto.
Por alguna razón, esto tiene errores.
Y específicamente, el error es "unused variable n".
E inexplicablemente, en la línea 11 tengo un "use of... n".
Entonces, no se usó aquí, pero se usó acá.
Y por alguna razón a la computadora no le gusta esto.
¿Por qué ocurre eso?
Sí
PÚBLICO: [INAUDIBLE]
DAVID: sí, allí está el truco.
Es un poco diferente de Scratch, pues cuando crean una variable
pueden usarla donde quieran.
En C y en otros lenguajes, las variables solamente
existen en lo que llamamos un determinado ámbito.
Y pueden pensar en un ámbito como las llaves
de apertura y cierre utilizadas más recientemente.
¿Qué es lo que implica eso?
En la línea nueve del lado izquierdo, estoy declarando una variable.
Oye computadora, dame una variable llamada n,
y almacenará un entero.
Esa es la historia que contamos.
Pero, el problema es lo que hago entre las llaves de las líneas 8 y 10.
Hoy afirmé que eso significa, que
en Scratch las piezas se rodean, y en C las variables
se tratan un poco diferente.
Si declaramos una variable aquí, ésta solo existe aquí,
y no la podemos utilizar aquí abajo, en nuestro código.
Esto parece ser un círculo vicioso.
Necesito una variable,
y entonces puedo declararla.
Pero no puedo declarar la variable allí, si la quiero usar más tarde.
En realidad, no parece una buena situación.
Lógicamente, incluso si nunca programaron antes,
si el mayor problema es que ésta variable solo existe
en el campo entre las llaves, de manera intuitiva, ¿cómo lo resolveríamos?
Sí
PÚBLICO: ¿Se mueven las llaves?
DAVID: Quitar las llav...
¡oh, se mueven las llaves!
Sí, podríamos mover las llaves, básicamente esa es la idea.
La trampa está en el bucle do-while, porque las necesita.
En casos generales necesitamos esas llaves.
Pero saben, ¿qué?
Ha pasado un tiempo desde que los escribí.
Pero, tengo otro par de llaves,
que están por fuera, por así decirlo, son mis llaves internas.
Así que, tengo otro campo aquí, el cual es
la función completa, llamada main.
Pero, ¿qué pasa si, de algún modo, declaro mi variable fuera de aquí?
Y de hecho, puedo.
Puedo ir a la línea siete, o incluso a una más arriba,
pero en general, queremos mantenernos lo más cerca posible
de lo que nos importa.
Puedo escribir: int n.
Y no creo que quiera indicar al usuario aquí,
porque crearía el mismo problema que antes, cuando solo
lo indicamos una vez.
Quiero esa indicación en un bucle, una, otra y otra vez, de manera exponencial.
Así que, está bien.
No hemos visto esto antes, pero podemos declarar una variable,
y después no hacer nada con ella.
Solo decimos, oye computadora, dame una variable,
Me ocuparé de ella después, al igual que en Scratch,
Si declaramos una variable,
lidiaremos con ella después, como queramos.
Ahora, esto todavía es un error.
No puedo decir, oye computadora, dame una variable n,
y después, oh por cierto, dame otra variable n.
Lo único que debo hacer para solucionarlo, es no volver a declararla,
solo usarla.
La línea siete dice, oye computadora, dame una variable llamada
n, que almacenará un entero.
En la línea 10, la misma historia de siempre, pero un poco más corta.
En el lado izquierdo esto dice: aquí está mi variable.
El lado derecho dice: este valor lo obtenemos del usuario.
Ponlo de derecha a izquierda.
Y ahora, porque n se declara o crea en la línea siete,
ésta existe en el campo de estas llaves más externas.
Y ahora, puedo usarla en cualquier lugar que quiera,
incluso en la línea 12, lo que es genial y lo más importante, en la línea 15.
Ahora guardaré esto, ejecutaré make mario3 y contendré la respiración.
Bien, funcionó, ./mario3.
Numero positivo.
No...
Le daremos -1
OK.
Le daremos cero.
Bien, muy bien.
Le daremos 3.
Ahora realmente coopera.
La construcción do-while todavía es un bucle,
y Scratch no tiene un análogo de esto.
El bucle do-while todavía es un bucle, pero hace algo al menos una vez.
La diferencia, fundamentalmente, sin embargo, es esta,
si hiciera algo como while (n <= 0),
y si cambiara esto a un bucle while, el cual vimos brevemente el otro día,
que es similar al bloque eterno en Scratch, si hago esto,
hay un problema lógico,
n fue declarada en la línea siete,
y así evitamos el problema del campo, desde el inicio.
Pero la línea ocho dice: while (n <= 0)
Pero, ¿qué es n en este punto?
Aún no está definida.
Como pronto veremos en el curso,
tiene algún valor basura, algún valor desconocido,
restos de lo que la computadora usó, esa memoria RAM o memoria, en el pasado.
Así que, este es un comportamiento indefinido, al parecer.
No sé si el bucle se ejecutará o no
porque no sé lo que hay en n,
Podríamos hackear esto, por así decirlo.
Generalmente, hackear algo es una forma de encontrar
la solución, que tal vez no sea la más limpia, a un problema.
Y OK, iniciaré esto en, -1,000 porque
sé que es menor o igual a cero,
Entonces, hackear esto soluciona el problema lógico porque ahora
sobre la línea ocho, ¿-1,000 es menor o igual a 0?
Lo es.
Así que ahora, mi bucle se ejecutará al menos una vez
y entonces cambiará el valor de n.
Pero ¿de dónde diablos viene ése -1,000?
Esta solución poco elegante ya que es un horrible, horrible diseño,
a pesar de que trabaja de manera lógica y es correcta,
es un mal, mal diseño.
Por eso comenzamos con un diseño mejor, un bucle do-while,
pero sabemos que hay muchas maneras diferentes de hacer las cosas.
Y podría ser que no, en una serie de problemas,
una o dos cosas no se hagan bien o de la manera correcta, la primera vez.
Eso está bien porque con práctica y experiencia,
comenzaremos a ver patrones, con los cuales
podremos resolver los mismos tipos de problemas.
¿Alguna pregunta sobre estas aproximaciones de Mario?
Bien, permítanme hacer una última, una última
relacionada con Mario y algo como esto.
Pasé demasiado tiempo buscando por las piezas de Mario
que describieran estas imágenes.
Y encontré esto, estos bloques subterráneos extra en el nivel de fuego.
Supongamos que quisiera imprimir un cubo de hashtags,
no solo una línea horizontal, ni una línea vertical,
sino juntar ambos tipos.
De hecho, podemos pensar en estos bloques exactamente como eso.
Que son: hashtag, hashtag, hashtag, hashtag, hashtag, hashtag, hashtag,
hashtag y así sucesivamente, como una máquina de escribir vieja,
que imprime una línea a la vez.
Si aún recordamos las máquinas de escribir,
pensemos que las computadoras y printf se comportan de manera muy similar.
Podemos imprimir algo y poner diagonal invertida n (\ n).
Imprimir algo más y poner la diagonal invertida n (\ n).
Entonces, ¿cómo se hace un cuadrado como el de la pantalla?
El proceso es como si pintáramos
la pantalla, de izquierda a derecha, moverse hacia abajo,
de izquierda a derecha y luego hacia abajo.
¿Cómo hacemos esto?
¿Cuál fue el tipo de código que usamos
para hacer algo una y otra y otra vez?
El primero fue el bucle for,
Podríamos usar otras construcciones, pero utilizaré el bucle for
de nuevo.
Guardaré esto como mario4, nuestro cuarto y último ejemplo.
Mantendré este código aquí, porque todavía
quiero indicarle al usuario una cantidad de bloques, en el número positivo de ahí.
No quiero simplemente hacer esto, pero veré dónde lo dejé.
A continuación, ejecutaré make mario4.
./mario4
Hagamos un bloque de 5 por 5.
OK, así no es,
esa es solo una columna.
Parece que debo hacer algo más.
Bien, al igual que en Scratch,
puedo tomar una idea y anidarla dentro de otra,
A continuación, haré esto.
¿Qué tal si dentro de mi bucle for que va de i hasta n,
hago otro for int? Pero no
quiero reutilizar i, porque siento que si la uso en dos lugares,
algo se desordenará.
Iré con el siguiente alfabéticamente, j,
lo cual es bastante común.
Entonces: (int j = 0;
j < n;
j++)
Ahora, pondré este bloque aquí,
creo que necesito deshacerme de esto de aquí,
y ahora veamos qué sucede.
Tengo un bucle for dentro de un bucle for,
si continúo y ejecuto mario4, Enter, el código es compilable,
./mario4
Escribamos 5.
Mmm
Creo que si los cuento esos en realidad son como 25,
eso no es lo que quería.
Yo quería un cuadrado.
Pero estéticamente, ¿qué es lo que obviamente falta?
Una línea nueva.
Pero pensé que esa, \ n, no iba aquí, ¿cierto?
Porque si hago esto, avanzará muy rápido.
Si ejecuto de nuevo Mario4 después de hacer ese cambio,
ahora hice el problema opuesto.
Entonces, ¿qué debemos cambiar?
Sí
Ohh, ¿solo rascarnos?
OK.
¿Qué debemos cambiar?
Sí, por acá.
PÚBLICO: Otra línea con un printf y una diagonal inversa n.
DAVID: Sí, ¿y cuál línea propondrían
para el printf con la diagonal inversa n?
PÚBLICO: ¿21?
DAVID: ¿Perdón?
PÚBLICO: 21
DAVID: 21
La ponemos ¿arriba o abajo de ella?
PÚBLICO: Arriba.
DAVID: Arriba de ella.
OK, voy para allá.
Proseguiré con printf ( "\ n");
Veamos ahora.
Entonces, make mario4
./mario4, Enter.
5
Hermoso, es hermoso.
En la pantalla, no se ven exactamente como un cuadrado porque los hashtag son
un poco más verticales que horizontales.
Pero, está bien.
También, construimos una aproximación de ese nivel.
Ahora, solo por si acaso, pensaré sobre
esta especie de simplificación excesiva, el imprimir tantos bloques.
¿Imprima esta cantidad de filas o columnas en el exterior?
Y luego , ¿a dónde vamos con esto? Print out this many ____
¿Cuál debería ser mi comentario aquí arriba?
¿Acá arriba son filas?
Y aquí abajo, estas deberían ser columnas.
Esto es porque en el bucle más externo tenemos a i,
Este comienza en 0 y al final irá hasta 5.
Siempre que i sea igual a 0, en el principio, es
como si el cursor se predeterminara a la parte superior izquierda de la pantalla.
Luego tenemos este bucle anidado, que dice: oh, por cierto,
haz lo siguiente cinco veces.
¿Qué haremos 5 veces?
Hashtag, hashtag, hashtag, hashtag, hashtag,
Entonces, una línea nueva,
después, i se convierte en 1,
esto es como moverse sobre,
lo siento.
Entonces, i se convierte en 1, lo cual significa que estamos en la segunda fila
porque imprimimos uno de esos caracteres en una línea nueva.
Aquí también, es donde los comentarios serían útiles porque
incluso yo tuve que pensar en eso.
Y no queremos perder el tiempo pensando
en el código que ya hemos escrito.
solo queremos la respuesta porque tomamos ciertas decisiones
en un caso como este.
Muy bien.
Supongamos que algo sale mal.
De hecho, ya resolvimos el problema de cómo muchos hashtags
nos llevarán de esta manera y muchos hashtags hacia esta otra.
Pero supongamos que queremos entender lo que nuestro código está
haciendo.
Tenemos otras dos herramientas que podemos usar,
ya que en la biblioteca CS50 tenemos una función que es casi
idéntica a printf, excepto que la llamamos eprintf, es como un error en printf
que nos ayuda a ver qué ocurre dentro del código.
y debemos utilizarla de la siguiente manera.
Si deseamos entender más claramente lo que
nuestro código hace, o hasta un ejemplo para el curso
que descargamos, desde luego, podemos añadirle nuestras propias líneas
de este, por ejemplo: "Hello there
I'm at home playing with this code" o algo por el estilo, ¿cierto?
Parece algo absurdo, pero al menos ahora
cuando veamos esa frase en la pantalla, sabremos en qué línea
se estaba ejecutando nuestro programa.
Podemos ser más metódicos respecto a esto.
Notemos las cosas que podemos hacer con eprintf,
Cambiaré esto a eprintf y funcionará igual.
También añadiré esto,
"about to prompt user for a number"
Solamente quiero proporcionarme una nota explícita y temporal
acerca de lo que está pasando aquí.
Veamos ahora lo que sucede
si ejecuto make mario4.
OK ./mario4
¡Ah!
Obtuve una salida un poco fea, pero solo es un diagnóstico,
es algo temporal.
Dice que mario4.c en la línea 10 tiene el siguiente mensaje:
"about to prompt user for a number"
Es una nota para mí, para que me sienta cómodo al comprender el flujo
o la estructura de mi programa,
todavía puedo interactuar con él.
Escribiré aquí algo como -1.
Si escribo -1 ¿qué debería ver después en la pantalla?
Sí, otra indicación,
"About to prompt user for number"
Es algo así como un examen médico,
si creemos que algo puede suceder, preguntémonos a nosotros mismos
si debería estar en el código y debemos asegurarnos de que vemos lo que esperamos.
y cuando estemos seguros de que el código está bien,
no debemos enviarlo con esta nota, porque no es
correcto por la especificación.
En este punto podemos deshacernos de esto.
Pero eso se vuelve tedioso muy rápidamente.
Ah, y ¿cómo puedo eliminar a mi programa si ya no quiero seguir jugando?
El comando Control-C finalizará el programa.
Existe otra herramienta, que quizás sea la más poderosa.
Y no puedo enfatizar sobre la importancia de esto, lo suficiente,
de adquirir el hábito de usarlo, cuando sea necesario, desde el principio.
Aún si nos toma 10 minutos extra, o inclusive media hora practicarlo,
posiblemente nos ahorrará horas durante el semestre.
Se trata de un programa llamado el depurador.
Entonces, un depurador es un programa que nos ayuda
a eliminar los errores de un programa.
Y funciona de la siguiente manera.
Voy a compilar de nuevo mario4.
Y lo más normal sería que lo ejecutara, desde luego, con ./mario4.
Pero supongamos que tengo un error y quiero entender lo que está pasando.
Haré lo siguiente.
Notarán que todos los ejemplos que les he mostrado
tienen números de línea del lado izquierdo, en el margen interno del programa,
y podemos hacer clic a la izquierda de estos números, en algo como
este punto de aquí.
Y podemos poner un punto rojo,
éste será conocido como un punto de interrupción,
es como una pequeña señal de alto, solo para nosotros, que dice
oye computadora, haz una pausa o detén totalmente mi programa aquí,
como una señal de alto temporal.
Entonces, el humano hace las cosas a una velocidad humana,
no a una velocidad de miles de millones de cosas por segundo
y con esto, me refiero a lo siguiente.
Ahora, no ejecutaré mario4, sino debug50 ./mario4, el cual
es un programa que escribimos, que llama o inicia
el depurador interno del IDE.
Noten que, como por arte de magia, acaba de aparecer este panel derecho
y en realidad siempre ha estado ahí.
Esto siempre dijo "depurador" y acaba de abrir esa ventana para mí
automáticamente.
Veamos lo que ocurre.
Hay un montón de palabras, pero ya conocemos muchas de ellas.
Observen que aquí abajo tenemos las palabras Local Variables,
Y luego aquí, hay una especie de tabla,
que no es muy grande porque solo hay una variable local.
Y en este punto de la historia, resulta que con mi variable n,
tengo suerte,
al parecer tiene un valor predeterminado de 0.
No debería confiar en eso.
Pero como mi programa no está tan desarrollado parece seguro,
ah más bien, mi programa está tan poco desarrollado que
tengo el valor 0 en esto, para nuestros objetivos de hoy.
Y es de tipo int.
Pero lo más genial es lo que sigue.
Observen que mi programa está pausado en la línea siete,
o específicamente en la línea 10, la cual es la primera línea interesante.
Por eso está resaltada en amarillo.
Pero lo genial aquí es esto,
arriba en la parte superior derecha, hay un botón de reproducción que indica
reproduce la siguiente parte de mi programa.
Reprodúcelo sin pausas.
O, si ponemos el cursor en Step over this line, pasaremos
sobre esta línea, lo que significa, oye computadora, reprodúcela
pero a mi ritmo humano, solo una línea del código a la vez.
Si fuéramos curiosos, podríamos entrar en esa línea del código,
pero veremos más sobre eso en un momento.
Mientras tanto, nos hemos adentrado más en este, que es el paso de salida
¿Y qué quiero decir con todo esto?
En este momento tengo una pausa en la línea 10, la cual
fue la primera línea interesante en mi programa,
en lo que respecta al depurador.
Continuaré en la parte superior derecha
y haré clic en Step Over.
Observen que mi ventana de terminal me pide un número.
¿Por qué?
Bien, porque pasé por encima de la línea get int, que significa ejecutarlo.
A continuación, escribiré ese número.
Escribiré -50, Enter.
Y estaré atento a la variable del lado derecho.
Observemos que en el depurador, aún sin imprimirlo con printf o eprintf,
Puedo ver que n tiene un valor de -50.
Es como un examen médico, por así decirlo.
Puedo ver lo que es, para asegurarme de que es consistente con mis expectativas.
Muy bien.
Eso no es correcto así que daré un paso más,
noten que la línea amarilla se movió porque está en un bucle,
pueden ver que lo sigo haciendo con mi mano.
Permítanme hacerlo de nuevo,
OK, positive number,
esta vez voy a cooperar,
42, Enter.
Observen que en el lado derecho, el valor de n es 42
y noten que si continúo pasando, la línea amarilla
está por saltar a la siguiente línea interesante del código.
Y si continúo haciendo esto, continúo haciendo esto,
miren lo que va a suceder en la parte inferior de la ventana de terminal azul.
Aquí está el primer hashtag,
aquí está el segundo hashtag,
es el tipo de animación falsa que hice el otro día con mis diapositivas
y lo que trato de hacer verbalmente, y con mi mano de atrás hacia adelante,
ahora lo podemos ver más cuidadosamente.
Incluso si escribimos un programa simple o solo un código,
podemos ver paso a paso lo que nuestro programa está haciendo.
Puede que no esté haciendo lo que esperamos,
pero lo veremos visualmente.
Muy bien.
Para continuar diré: OK, imprime el resto del programa
y presiono reproducir.
Veremos que el depurador GDB o GNU sale del servidor,
se está cerrando.
Ahora estoy de nuevo en mi línea y el depurador desaparece.
Por lo que no menosprecien esas herramientas especiales.
Antes de continuar, creo que debería presentarles a Abhishek,
tal vez lo vieron en internet hace un par meses,
se volvió algo viral.
Es un recién graduado de la Universidad de Nueva York,
e hizo esta cosa extraordinaria.
Tomó el llamado Microsoft Hololens, el cual es un dispositivo de realidad
aumentada que pone una especie de pantalla ridícula frente a los ojos.
Pero entonces, proyecta las imágenes frente a nuestros ojos
y actualmente es tan genial como un teléfono Android o un iPhone,
el aparato sabe dónde estamos, en un espacio tridimensional.
Pero lo que Abhishek hizo fue que entró a un espacio muy tridimensional,
el Central Park en Manhattan.
Ya había pasado días recreando a "Súper Mario Bros.".
en realidad aumentada al producir uno de esos mapas a los que
me referí anteriormente.
Le daremos solo un vistazo al resultado final
y lo pondremos en el sitio web del curso para que lo vean a detalle más adelante,
fue este, que fue bastante alucinante y una aplicación maravillosa de las ciencias
de la computación en el mundo real.
[REPRODUCCIÓN DE VIDEO]
Hola,
soy Abhishek
Y recreé el emblemático primer nivel de "Súper Mario Bros"
en un juego de realidad aumentada, en primera persona y de tamaño natural
que ahora voy a jugar como Mario.
[MÚSICA, TEMA DE "SÚPER MARIO
BROS"]
[FINALIZA LA REPRODUCCIÓN]
DAVID: Abhishek dio una charla en CS50 sobre tecnología, un par de meses atrás.
Y la parte más divertida, si miramos detenidamente y estamos en Manhattan,
es que algunas personas lo miran.
Pero muchos neoyorquinos ni siquiera miran dos veces lo que él hace.
A continuación, tomaremos un descanso de cinco minutos
y cuando regresemos, comenzaremos a ver el mundo de la criptografía.
Estamos de vuelta.
Desde luego, existen más funciones que printf.
Y les dimos un vistazo con ayuda de la biblioteca de CS50,
también hay muchos, muchos, muchos, muchos más que vienen incluidos en C
y que personas en todo el mundo, han escrito a lo largo de los años.
Pero implícitas en cada una de estas funciones de CS50,
noten a estas palabras clave como string, int y float,
de las cuales también ya hablamos,
long, long, long y double, como también vimos antes.
Entonces el lenguaje C, tiene lo que llamamos tipos de datos
y los descubrimos el otro día.
Estos especifican qué tipo de datos podemos poner dentro de una variable.
Y esta también es la diferencia con Scratch.
En C y en otros lenguajes también tenemos
que decidir previamente, como programadores, qué tipo de datos
pondremos en esta variable, de modo que la computadora
o en realidad el compilador lo sepa,
así el compilador sabrá como lidiar con eso para nosotros.
Bueno, si deseamos imprimir estas cosas,
printf también viene con ciertos códigos de formato.
Hemos visto %s para cadenas y %i para los números enteros.
Aunque también existen muchos otros.
Quizás los más comunes serían estos y seguramente los han visto,
%f para float,
lo vimos el otro día.
%lld para un número decimal muy muy grande.
Ese es uno de los que a menudo tengo que buscar yo mismo.
E incluso existen muchos más de estos, demasiados.
Noten que mientras recibimos la información de los usuarios,
ya sea para series de problemas o con cualquier otro propósito,
algunas veces debemos revisar el manual o la documentación,
por así decirlo, para las funciones que utilizamos.
Y de modo que sepamos a dónde acudir para ese tipo de cosas,
permítanme presentarles algo rápidamente.
Verán más de esto en apartados adicionales, secciones y más.
Si, por ejemplo, olvidamos cómo funcionan ciertas funciones,
podemos escribir lo siguiente, "man get_string"
donde man representa el manual.
Este es un viejo comando de las computadoras Unix y Linux,
que tiene un entorno de teclado basado en texto.
Verán un manual del usuario mucho más estándar y más estructurado
para la función que les interese.
De modo que, si olvidan lo que hablamos en la clase
o no están seguros de cómo podrían utilizarlo
y la función es algo como get_string,
aquí pueden leer sobre ella.
Pero algunas veces, esto parecerá un poco anticuado,
es decir, aún no hablamos del significado de algunos de estos símbolos, la..
la palabra const, el asterisco que he resaltado en la pantalla.
Algunas veces encontrarán que las páginas man, como se le conoce
a las páginas del manual,
son confusas entre sí, lo cual es una situación desagradable.
Si estamos confundidos y la documentación no nos sirve de ayuda,
necesitaremos de una tercera opción.
De hecho, si van a la página web de CS50,
encontrarán un enlace a una herramienta
que el equipo ha creado en estos años, llamado CS50 Reference.
Es una versión más fácil de utilizar de esas mismas páginas
man, donde revisamos y traducimos
el inglés anticuado, a uno menos cómodo, por así decirlo.
Entonces, si por aquí me desplazo hacia, digamos, printf
o en vez de eso, simplemente lo busco,
puedo ver aquí a printf,
está dentro del encabezado de este archivo, este archivo de h del sistema
y ahora puedo leer sobre esto aquí.
Observen la parte superior derecha, está marcada la casilla Less Comfortable,
lo que significa, oye, muéstrame el lenguaje que el TFS generó en lugar
del lenguaje predeterminado.
Pero, esto también es una rueda de entrenamiento.
Y cuando estemos listos para aprender
algunas de estas simplificaciones, podemos desmarcar la casilla
y ver una versión técnica mucho más detallada
de la que encontraríamos en el mundo real.
Debemos tener esto en cuenta, en especial
si sentimos que avanzamos muy rápido en la clase,
lo cual, de hecho, hacemos y deben apoyarse en algo confiable de aquí en adelante.
Pero averigüemos lo que es una cadena.
Comenzaré aquí con Stelios.
Así que Stelios, uno de nuestros jefes de TAs en New Haven, tiene este nombre.
Y lo escribí como una cadena, S-T-E-L-I-O-S.
Pero dibujé las casillas a propósito
alrededor de su nombre, para resaltar que esta cosa que llamamos cadena,
como "Stelios" no solo es una cadena,
en realidad es una abstracción para algo
pequeño de nivel inferior, es decir, un carácter después de un carácter
después de un carácter y así sucesivamente.
Aquí también vemos un ejemplo de una abstracción.
No es tan divertido llamar a Stelios, S-T-E-L-I-O-S. Lo llamamos Stelios.
Pero en lenguajes como C, a esa construcción la llamaríamos una cadena
o más técnicamente, una secuencia de caracteres.
Pero es una cadena,
es una buena abstracción
y una buena simplificación.
Ahora, aquí tenemos una oportunidad
para ver la relación entre los caracteres y los números en una computadora,
y lo poderosos que son los programas y el software que nosotros mismos
podemos escribir.
Pero primero, ¿cómo introducimos cada uno de los caracteres de un nombre?
Con la función get_string, como vimos, puedo tener fácilmente el nombre Stelios,
al igual que lo hizo Sam con el público el otro día.
Pero, ¿cómo puedo obtener la S, la T o la E?
O si él hace un error tipográfico o no lo escribe cuidadosamente,
¿cómo puedo capitalizar su nombre o limpiar su entrada de usuario
como los sitios web actuales, que lo hacen muy a menudo?
Bien, para continuar abriré de nuevo el IDE de CS50
y haré un ejemplo bastante simple, que esta vez involucra cadenas.
Crearé un nuevo archivo,
lo llamaré string0.c.
Ahora voy a escribir un programa corto,
vamos! una vez que he perdido el control sobre mi ventana de terminal,
ahora he perdido el control de mi menú.
Esto es culpa mía por..
oh, aquí vamos.
Bueno, esto se verá genial.
Esto es muy inspirador.
¿A dónde se fue?
Oh, oh.
Aquí está.
OK.
Este es un ejemplo de un mal diseño, de modo que lo arreglaremos.
Y ahora veo que escribí mal string, lo puse como strig,
Así que solo vamos a,
nadie en internet sabrá nunca, que esto sucedió.
OK, string0, voilà!
Aquí vamos.
Muy bien.
Así, string0.c y prepararé rápidamente un programa como sigue.
Entonces, int main(void)
y ahora string s = get_string
pediré la entrada del usuario así: string s = get_string("input: ");
Continuaré con printf.
¿Qué tal si ponemos la palabra output aquí? printf("output");
Solo para que sea agradable y ordenado, pondré un par de espacios
con anticipación aquí.
Sobre la línea 5 haré lo siguiente,
mi intención es obtener el nombre de Stelios de él
o de quien sea que esté jugando este juego.
Pero no solamente quiero imprimir algo como
"hello Stelios" e introducir su valor s, el cual hicimos antes.
Quiero hacer este carácter al mismo tiempo.
Pero hacer algo "una vez al mismo tiempo", sugiere un bucle.
Y de hecho, puedo hacer uno.
Así que haré for (int i = 0; i < however
for (int i = 0; i < however long his name is; i++)
Ahora les presentaré otro truco al que
pueden darle un vistazo muy rápido desde la pantalla que tenía antes.
Resulta que %c es el marcador de posición para un carácter,
quizás esto no les sorprenda,
pero el truco es que solo tengo acceso a s, a la cadena completa de s.
Aunque parece que hay una nueva pieza de sintaxis aquí
y en cierto modo implica el que usáramos casillas
para rodear las letras del nombre de Stelios aquí
y parece que el equivalente en C, es muy parecido a hacer esto,
hacer una casilla de caracteres con los corchetes, los cuales es posible que no
utilicen con frecuencia en su teclado.
En un teclado estadounidense, a menudo están encima de la tecla Enter.
Para continuar, escribo s[i]
Y por así decirlo, se imprimirá el carácter i,
del nombre de Stelios.
De modo que la i comenzará en 0
y continúo haciendo más, más, más, más, más, más,
y usando la notación del corchete, por así decirlo,
en este caso puedo profundizar en cada una de las letras de su nombre.
Por lo tanto, cuando ejecuto esto, ¿cuál va a ser el resultado?
A continuación, ejecutaré make string0.
Ajá.
OK, eso no es un código válido en C, sin importar cuan largo sea su nombre.
Así que tengo un problema que resolver aquí.
¿Cómo obtengo la longitud de su nombre?
Bien, puedo hacer una especie de trampa.
OK, así que uno, dos, tres, cuatro, cinco, seis, siete.
Muy bien.
Podemos escribir este programa como sigue,
7
Pero esto nos llevaría por el camino equivocado.
¿Por qué esta no es una buena solución al problema?
¿Sí?
PÚBLICO: Porque no es intercambiable.
DAVID: Exactamente,
no es intercambiable.
Tengo este dinamismo en get_string para obtener nombre de Stelios.
Pero siete no será verdadero para todas las personas que usen este programa.
Necesito algo dinámico.
Bueno, existe una función para eso,
La puedo llamar strlen por string length pass in as input
la variable cuya longitud quiero obtener y que
me devolverá un número, que en este caso sería el 7.
Pero será dinámica,
de modo que si escribo algo como David, con suerte, nos devolvería 5
y cualquier número, para cualquier cantidad de personas que participen así.
Lo intentaré nuevamente,
make string0
un montón de errores
y "use of undeclared identifier string"
esperen un momento,
ya hemos visto esto antes.
¿Cómo lo resolví la última vez?
¿Sí?
¿Qué falta aquí arriba?
PÚBLICO: Las bibliotecas.
DAVID: Sí, las bibliotecas o el encabezado
de los archivos, por así decirlo, para las bibliotecas.
Así que necesito #include y estoy muy seguro de para printf.
Necesito #include para get_string
Ya casi lo logramos.
Veamos si eso es suficiente.
make string0.
Oh, Implicitly declaring library function 'strlen' with type..
Realmente no sé qué esto,
pero allí nos insinúa una especie de respuesta
include the header string.h, entre otras cosas
Así que parece que esto es verdad
y existen diferentes maneras de comprobarlo.
Si regreso a reference.cs50.net y busco strlen,
aquí está esa función.
Permítanme volver a la menos cómoda, wups!
a la versión menos cómoda.
Observen que debajo de la sinopsis de una página man o de reference.cs50.net
siempre hay un breve resumen de cómo usarla.
Así, el prototipo de la función que
nos da una idea de lo que es, size_t, básicamente equivale a un int,
que lo único que nos indica es el tamaño de algo, como un número,
pero incluye a string.h, que es el ingrediente que quería.
Así que copiaré esto.
Permítanme regresar al IDE.
Me pondré un poco quisquilloso
y mantendré acá arriba un orden alfabético,
aunque no es estrictamente necesario,
pero nos facilitará echarle un vistazo después, cuando la lista se alargue.
make string0.
Parece buena idea continuar ./string0
Input.
Escribiré el nombre de Stelios.
Y me dio también su salida.
Imprimir su nombre fue mucho trabajo innecesario.
Simplemente podría haber utilizado %s.
Pero ahora puedo hacer modificaciones.
¿Y si quisiera imprimir uno por línea?
Puedo añadir eso.
Puedo poner el programa otra vez, volverlo a ejecutar y escribir su nombre,
y ahora me da uno por línea.
Se ve un poco feo.
Ahora tiene s como salida,
pero solo es un error estético.
podría volver y arreglarlo,
pero ahora tengo el control de los caracteres individuales
en su nombre.
Parece que de alguna forma hemos progresado,
pero si ahora tengo acceso a las letras individuales,
podemos volver en círculo a la primera lección, en donde
hablamos sobre los ceros, los unos, y después los números
y las letras, y ahora, a su vez, de las palabras, también conocidas como cadenas,
con ayuda de un tema llamado conversión de tipos.
Los tipos, por supuesto, son los tipos de variables de los que hemos hablado.
Conversión significa convertir de uno a otro.
Y si lo recuerdan de nuestra primera lección,
esa A mayúscula era el número que en decimales conocemos como 65,
y cualquier patrón de ceros y unos que tenga.
La B mayúscula es el 66, y así sucesivamente.
Ahora, ¿puedo verlo por primera vez ?
Bueno, resulta que sí puedo.
Regresaré al IDE
y crearé un nuevo programa llamado ascii0.c.
y ASCII, nuevamente, solo es el estándar.
Es el acrónimo de American Standard Code for Information Interchange,
el cual asigna números a letras y letras a números.
A continuación, escribiré un programa rápidamente.
#Include stdio.h, para printf.
int main(void).
Y luego aquí haré lo siguiente.
¿Saben algo?
Imprimiré, digamos, string s = get_string("Name: ");
Vamos a preguntarle el nombre de alguien.
A continuación escribiré: for (int i = 0;
i es menor que la longitud de esa cadena,
de acuerdo con lo que vimos la otra vez,
i++.
Y esto va a iterar sobre la cadena entera.
Ahora quiero hacer esto,
quiero imprimir lo siguiente,
imprimiré el mismo carácter y luego un espacio,
y después, qué tal un número entero, y una nueva línea.
En un momento veremos qué es lo que hace.
Quiero conectar los valores para estos marcadores de posición.
Pero, ¿cómo puedo obtener el primer carácter del nombre, si la cadena se llama s?
Sí, entonces s[i] para el carácter de i.
Y esto conectará a S-T-E-L, si Stelios es el que participa
en el juego.
Pero ahora pongo una coma para conectar un segundo marcador de posición.
Y %i, ¿saben lo que voy a hacer?
Pondré un (int), en paréntesis, y s[i];
Esto se ve un poco enigmático,
pero eliminaré por un momento esta parte.
Se trata de lo mismo, pero escrito dos veces,
imprime el carácter i del nombre, el carácter i del nombre.
Pero hago entre paréntesis, lo que se llama conversión de tipos.
Tomo cualquier cosa que sea, es decir un char o un carácter.
Y digo entre paréntesis, haz que en vez de aquello se vuelva un int.
Entonces, si es una A mayúscula, se convertirá en 65,
la B mayúscula se convertirá en 66 y así sucesivamente.
Y si ahora compilo este programa después de arreglar, de manera preventiva,
lo que habría sido un error y agrego el encabezado,
make ascii0.c, ¡ups!, perdón.
¡Oh! es un error común.
Nada que hacerle.
Estoy bastante seguro de que falta hacer algo.
Necesito compilarlo.
¿Qué hice mal?
Sí, no debo poner el .c,
Es algo contraintuitivo, pero cuando queremos hacer un programa,
escribimos el nombre del programa, no el nombre del archivo.
Ahora, en... ¡Oh, diablos!
Nunca aprendo de mis errores.
¿Qué me falta ahora?
PÚBLICO: El string.h.
DAVID: Sí, el string.h.
Muy bien.
include string.h.
Lo guardo.
OK, ahora ejecutaré make ascii0.
Muy bien, ./ascii0.
Y ahora, Stelios, Enter.
Ahora vemos los códigos ASCII o los números que
corresponden a las letras de su nombre.
Son números muy grandes,
ahora están en los cientos.
Y eso se debe a que son minúsculas.
Ya hablamos antes de la A mayúscula, la B mayúscula y así sucesivamente.
Pero las letras minúsculas también
tienen valores asociados a ellas, como algunos de los que vemos aquí.
Y ahora que sé esto, ahora que lo conozco,
puedo hacer las cosas de bajo nivel que damos por hecho en nuestros teléfonos
y en los sitios web, como cuando escriben su nombre en puras letras minúsculas
y el sitio web lo corrige, o si escriben su número de teléfono
con paréntesis, sin paréntesis, con guiones, o sin ellos,
el sitio web simplemente lo corrige y lo pone en algún formato más limpio.
Ahora tenemos una especie de control a bajo nivel para hacerlo.
No lo escribiré manualmente, porque es un poco largo,
más bien lo abriré para continuar.
Y entre los ejemplos que veremos hoy en source2, que es el sitio web del curso,
está este ejemplo,
capitalize0.
Haré un poco más de espacio para esto,
ya que es un poco largo.
Vamos a enfocarnos en un par de líneas a la vez.
Aquí está el inicio de mi programa, main,
aquí está la línea de código que corresponde a get_string, before,
solo digo, dame la cadena before.
Y digo, imprime la cadena after, luego de que se le hagan algunos cambios.
¿Y qué haré ahora?
Parece que en la línea 11 utilice algunas de las mismas ideas,
pero con un cambio.
Hice algo diferente.
La línea 11 es muy similar a lo que apliqué para iterar
los caracteres de una cadena.
Pero hice algo diferente, ¿qué fue?
¿Qué se ve diferente en comparación con lo que había antes en la pantalla?
¿Alguien que esté un poco más atrás?
Sí.
PÚBLICO: [INAUDIBLE]
DAVID: Sí, al parecer declaro dos variables
al mismo tiempo, por así decirlo.
Tengo mi (int i = 0) y hemos hecho esto muchas veces.
Pero ahora, por primera vez, tengo una coma aquí.
n = strlen(s).
Pero si creen que estos componentes básicos son...
OK, la coma es nueva, pero n está a la izquierda, entonces es una variable.
Probablemente es un int, porque la palabra int vino antes.
El signo igual es una asignación de derecha a izquierda.
strlen es una función que devuelve la longitud de una cadena.
Y para que quede claro, esto parece almacenar, ¿cuál número en n?
Sí, el número de caracteres en cualquier
cadena en la que el usuario haya escrito, "Stelios", "David",
o cualquiera que sea el nombre.
Entonces tengo mi condición.
Después está el punto y coma, que ya hemos visto.
Y luego está el ++.
Yo opino que este tiene un mejor diseño en cierto sentido,
es un poco más complicado,
ya que escribí más caracteres
y añadí otra variable.
Pero ¿por qué es inteligente y es buen diseño
utilizar una variable adicional y usar más espacio para guardar
un número, simplemente para comparar i con n?
¿Por qué salté a través de estos aros?
¿Qué es lo que piensan?
PÚBLICO: Porque entonces no tiene que comprobar
qué es n cada vez que pasa por el bucle.
DAVID: Exacto.
PÚBLICO: [INAUDIBLE]
DAVID: No tiene que comprobar cuál es la longitud de la cadena
en cada iteración porque, al final, una vez que Stelios o cualquier cosa que escriba
en su nombre, no cambiará,
ya sea el nombre de D-A-V-I-D o Stelios o María o quienquiera que esté jugando
este juego.
Pero, ¿por qué seguimos llamando a una función repitiendo... oh,
por cierto, ¿cuál es la longitud?
¿cuál es la longitud?
Recuérdenlo a la primera vez, porque es probable que tome algo de tiempo
hacer ese cómputo y averiguar la longitud.
Simplemente mantenemos esa respuesta alrededor de n
y podemos comparar dos variables.
Mientras tanto, aquí tenemos una gran construcción: if-else.
Pero si lo partimos en pedazos, hará algo relativamente sencillo.
En la línea 13 pregunto si el carácter i de s es mayor
o igual a una A minúscula, entonces &&, un símbolo que no vimos antes,
lógicamente significa y.
Entonces, si es mayor o igual a "a" y menor o igual a z,
dicho de otro modo, si está entre a y z, incluso
en minúsculas, ¿qué estoy haciendo en la línea 15, la cual se ve súper rara?
En primer lugar imprimo %c, el cual es mi marcador de posición,
Pero después imprimo el resultado de s[i] menos cualquier minúscula A, menos
la A mayúscula.
Es decir, esto es muy extraño.
Pero les puedo dar una pista.
Resulta que aquí hay un patrón
y los seres humanos lo hicieron deliberadamente.
Si pueden hacer el cálculo rápidamente, ¿qué tan lejos
está la A minúscula de la A mayúscula?
Es 32, ¿cierto?
Y podríamos calcular
97 menos 65, oh, es 32.
¿Qué tal la B mayúscula contra la B minúscula?
Es 32.
32.
De modo que sigue este patrón,
por así decirlo, y es una especie de demostración mediante un ejemplo.
Aún no vemos todo el camino hasta la Z, pero créanme.
el 32 es invariable para todas las letras del alfabeto.
Siempre están a una distancia de 32.
Y apliqué un hardcode a 32, pero luce poco elegante.
En vez de eso, por qué no solo digo aritméticamente,
cualquiera que sea la diferencia entre la A minúscula y la A mayúscula,
y eso es lo que digo aquí entre paréntesis.
Cualquiera que sea la diferencia numérica en la representación computacional
de mis números, resta esa diferencia del carácter i.
Ahora, lo bonito es que primero debería hacer esto,
debería proyectar el carácter hacia un int.
Pero no necesito ser tan explícito.
La computadora sabe que los caracteres son números enteros,
y sabe que los números enteros son caracteres.
Existe esta equivalencia.
No necesito dar tantos detalles al respecto.
Basta con dejar que la computadora averigüe,
implícitamente, que en este contexto, hago cálculos con los números,
y luego, en el contexto de printf, muestro ese número como caracteres.
Como no pasa nada,
le estamos diciendo al compilador el contexto
en el que debe tratar estos valores numéricos o caracteres.
Para abreviar la historia, ¿qué efecto tienen estas cuatro líneas de código
en los caracteres de la entrada del usuario?
¿Qué hace?
PÚBLICO: [INAUDIBLE]
DAVID: Los vuelven mayúsculas.
¿Cierto?
Los vuelven mayúsculas.
Y por el nombre del archivo, tiene implicaciones, en la información completa.
Pero así es como resolvemos el problema con el uso de las mayúsculas.
Aquí está la cadena.
Enfoquémonos en los caracteres individuales.
Averigüemos si están dentro del rango que deseamos manejar.
Y si es así, apliquemos algún tipo de mutación para cambiar de un valor
al otro.
Podía haberlo hecho de un modo horrible.
Podría haber tenido algo como if-else-if-else-if-else,
Podría haber tenido como 26 condiciones para comprobar, ¿es el carácter A?
¿es el B?
¿es el C?
Y si es así, conviértelos en A mayúscula, B mayúscula, C mayúscula.
Pero ese código sería tanto o incluso más grande que este.
Esta es una forma más algorítmica para resolver el mismo problema.
Y si no es una letra minúscula entre A y Z, imprímela.
No hay nada más que hacer.
Ahora, tal como lo vimos, no tiene que ser tan detallado.
En capitalize1.c, el cual está disponible también en el sitio web del curso,
mejoré un poco el diseño de mi código.
No estoy reinventando la rueda,
solo me apoyo en los hombros de programadores más inteligentes.
Y por lo menos cambié una cosa.
En vez de hacer manualmente este proceso de comparación con la A minúscula
y la Z minúscula, solo tomo impulso y utilizo
una función que, maravillosamente, se llama islower, la cual
responde a esa pregunta.
Porque los otros tipos de datos en C, no solo son
int, char, float y string de la biblioteca de CS50,
sino que también hay algo que se llama valor booleano.
Un valor booleano, se llama así en honor a Boole y es similar en esencia
a una expresión booleana verdadera o falsa.
Pero una variable booleana solamente es la idea de verdadero o falso.
Y pensaríamos que islower devolverá un valor booleano.
Devuelve verdadero o falso, sí o no
y el nombre de esta función, por lo tanto, es muy apropiado.
¿Islower?
Esa es una pregunta de sí o no, verdadero o falso.
No sé cómo se implementó.
Si realmente me importara, podría consultar la referencia de CS50
o podría usar el comando man en el IDE
y comprobar realmente cómo funciona esta cosa.
Pero necesito una conclusión.
Para utilizar esto, necesito utilizar la biblioteca de ctype.
Hay otras bibliotecas en las que por el momento solo arañamos la superficie
y solo sabríamos que existen al leer una documentación como esta.
¿Pero saben algo?
Puedo ir más allá.
¿Saben algo?
Si hace años un humano escribió una función para ver si algo está en minúscula,
¿qué fue lo que probablemente, también hizo para mí?
PÚBLICO: ¿Las mayúsculas?
DAVID: Sí, también existe isupper.
Sí.
Haré un spoiler aquí.
Existe isupper.
Pero se arriesgaron al comprobar si es mayúscula o minúscula.
¿toupper?
Sí.
Resulta que hay una función llamada toupper
que convierte una letra en mayúsculas.
De hecho, puedo aprovecharla en mi tercera versión del programa
como sigue.
capitalize2.c tiene un mejor diseño, por así decirlo.
es incluso más corto, tiene menos líneas de código, es más fácil de leer
y hay menos oportunidades para cometer errores.
¿Ahora cómo lo resuelvo?
Aún hago iteraciones sobre cada uno de los caracteres,
pero a ciegas designo toupper, toupper, toupper sobre cada uno de los caracteres
porque leí la documentación.
Si pasamos por toupper un carácter que ya está en mayúscula,
simplemente lo imprime,
no lo cambia.
Si pasamos un símbolo de puntuación, solo lo atraviesa,
pero lo pasamos por una letra minúscula, la vuelve mayúscula.
Y así, ahora puedo implementarlo
puedo apoyarme en quien implementó eso antes que yo,
podría haber sido yo,
podría haber escrito mi propia función toupper,
pero no necesité hacerlo, porque en el mundo de la programación,
existen bibliotecas de códigos que otras personas escribieron para nosotros
y que podemos aprovechar.
¿Tienen alguna pregunta al respecto?
Sí.
PÚBLICO: Entonces este método no se podría [INAUDIBLE].
DAVID: Serían todos, sí.
Si en Stelios quisiera convertir en mayúscula el primer...
la primera letra de su nombre, probablemente no querría el bucle.
Probablemente solo volvería mayúscula el [0] específico de las letras.
Pero me aseguraría de que su nombre tuviera al menos un carácter de longitud
por si acaso se le diera Enter de forma accidental o maliciosa.
Completamente.
Ahora vamos a sumergirnos en otro detalle de la forma siguiente.
Supongamos que quiero saber cuál es la longitud de una cadena.
Sé que existe esta función llamada strlen.
Pero resulta que también puedo averiguar la longitud de las cadenas por mí mismo.
Para continuar, escribiré un programa que se llame strlen,
pero en este ejemplo no se me permitirá utilizar la longitud de la cadena.
Incluiré la biblioteca de CS50,
también incluiré stdio.h,
aquí pondré int main(void)
y en esta parte escribiré string...
mal formato, string s = get_string("Name: "),
"Name: ".
y a continuación pondré int n = 0.
Dame una variable llamada n que sea igual a cero
y después pongo, while (I'm not at the end of the string),
también el código que no sea válido,
n++;
puedo utilizar el truco del ++ que ya vimos antes para i++.
Luego imprimiré cualquiera que sea el valor de este contador
porque quiero que mi bucle cuente
el número de caracteres que hay en el nombre de Stelios
o en el de cualquiera que ejecute el programa.
Como aclaración, esto es lo que se conoce como azúcar sintáctico,
es una forma muy sexy para decir que esto es una notación abreviada,
lo cual se ve más aburrido.
Hace exactamente lo mismo,
simplemente es una manera más breve de hacerlo.
Y verán pocas características en los lenguajes como esta
para salvar a los humanos de presionar más teclas.
Por supuesto, esto no es una solución a un problema.
¿Cómo sé que estoy al final de la cadena?
Bueno, tenemos que romper la capa de abstracción, por así decirlo,
de cadenas, solo un poco.
Pero en la computadora tenemos esta pieza de hardware,
la RAM,
la cual vimos el otro día.
Y hablamos un poco sobre las limitaciones de las computadoras
y la cantidad finita de memoria que tienen.
Y si pensamos en todos los chips que hay en este dispositivo,
por el momento no importa cómo funcionan,
pero sabemos que hay montones y montones de bytes que pueden
almacenarse en la memoria de la computadora,
tal vez mil millones de bytes, 1 gigabyte, dos mil millones de bytes, 2 gigabytes.
Pero para nuestros fines, solo piensen en la RAM dentro de la computadora
como una larga lista de bytes disponibles, muchos bits, ceros y unos,
a los que les podemos cambiar los valores.
Tal vez es una especie de cuadrícula, así que hay muchos bytes
horizontales y verticales.
Podemos clasificarlos a todos, así, un byte será el 0,
y aquel que está en la parte más inferior sería el byte número 2 mil millonésimo.
Supongan que podemos numerar todos los bytes en la memoria de una computadora,
Bueno, entonces cuando escribimos el nombre de Stelios
termina con una S, pero tal vez sería una decisión tonta buscar una S
al calcular la longitud del nombre de alguien
porque no va a funcionar en mi nombre,
no funcionará con el de María ni con el de otras personas que están en la sala.
Así que aún no sabemos bien lo que está pasando
dentro de la memoria de la computadora.
Si pensamos en la cuadrícula como la RAM de una computadora,
tal vez la esquina superior izquierda sea el byte cero,
la siguiente será el byte uno, luego el byte dos, luego el...
byte dos mil millones.
La estoy representando arbitrariamente como una cuadrícula bidimensional.
Necesitamos saber que existe este carácter especial.
Lo que C hace por nosotros, incluso sin que le digamos que lo haga,
es poner un número secreto al final de cualquier cadena que los humanos escriban.
Se representa específicamente como \0
y es la manera especial, al igual que la \n,
para decir que hay ocho bits cero juntos.
Es un valor especial 0.
Y ahora que tenemos este valor llamado centinela,
el valor centinela significa que es especial.
El humano no puede escribirlo,
al igual que no puedo escribir todos los bits cero en mi teclado,
porque, aunque presione el número cero, técnicamente
es el carácter 0, porque los números en el teclado
asignan números enteros diferentes.
Pero hablaremos de eso en otro momento.
Entonces 00000000 son bits o lo que sea eso.
Y si escribo un programa que llame a get_string varias veces,
y Stelios es el primero en escribir su nombre,
podría terminar viéndose así en la memoria.
Pero supongamos que otra persona escribe su nombre, por ejemplo María.
Su nombre cabrá en la siguiente memoria disponible,
pero también terminaría en nulo, por así decirlo.
Al valor centinela también se le llama nulo, N-U-L, pero solo tiene ceros.
Y luego, si alguien más escribe su nombre,
también encajará allí.
Por ejemplo, Zamyla,
se envuelve pero, una vez más, es una interpretación artística arbitraria
de la memoria de mi computadora.
Z-A-M-Y-L-A, \0.
Y puedo seguir escribiendo nombres hasta que me quede sin memoria.
En ese punto, el programa colapsará,
o tendré una condición if que dice demasiadas cosas en la memoria.
Algo tendrá que detenerse en ese punto.
Entonces, ¿qué es lo que esto significa para mi implementación?
Básicamente es lo siguiente.
Para continuar puedo cambiar este tonto inglés a lo siguiente.
while (s[n] !=
while (s[n] != '\0')
Y uso comillas simples esta vez porque, recuerden
que usamos comillas simples cada vez que hablamos de caracteres individuales.
Usamos comillas dobles cada vez que hablamos de cadenas.
Y aunque s es una cadena, s[n] es el n-ésimo o el i-ésimo, no importa cuál
letra utilicemos, el n-ésimo carácter.
Entonces es un carácter.
Y ahora tenemos que usar comillas simples.
Entonces lo que hace es lo siguiente, inicializa n en 0
y luego busca en la memoria.
Y dice, ¿esta barra invertida es 0?
Si no, increméntalo n en 1.
¿Esta barra invertida es 0?
No.
Diablos.
No.
Sí.
Y en este punto tengo 7 dedos arriba o n está almacenando el valor 7.
Eso es lo que mi programa imprimirá.
Entonces ahora tenemos un programa completo que cuenta
el número de caracteres de una cadena.
No necesito este programa porque strlen existe como una función,
pero ahora es una capacidad a la que tengo acceso.
Alguna pregunta entonces, sobre ¿cuál cadena
tiene bajo la manga esta secuencia de caracteres
con un carácter especial nulo al final?
Sí.
PÚBLICO: [INAUDIBLE]
DAVID: Ah, buena pregunta.
¿Qué pasa con otros tipos de datos, si puedo reformularlos como ints y floats?
y así sucesivamente?
De hecho, las cadenas son especiales.
Si me desplazo de nuevo a la lista de tipos de datos que tiene C,
por ejemplo, la mayoría de estos son de longitud fija
y esta es la razón por la cual el compilador necesita saber
lo que está poniendo en ellos, porque el compilador y la computadora
necesitan saber si es un byte,
si son dos bytes,
si son cuatro,
si son ocho.
¿Cuántos bytes debería ver?
Las cadenas no tienen una longitud predeterminada porque
no sabemos quién escribirá su nombre.
Pero en la mayoría de los sistemas un int siempre
será de 32 bits, de 64 bits, o, de manera equivalente,
de cuatro bytes u ocho bytes, porque hay una proporción de uno a ocho.
Un bool a menudo es un byte.
Es un desperdicio.
Aunque técnicamente se necesita un bit,
es más fácil lidiar con incrementos de ocho bits.
Los caracteres son, por definición, de ocho bits o de un byte.
Entonces, casi todos los tipos de datos tienen una longitud fija
y no se necesitará tener un carácter especial nulo,
sino las cadenas que hacemos.
Las cadenas son especiales.
¿Alguna otra pregunta?
Muy bien.
Entonces, ¿qué podemos hacer con esto?
Bueno, esta idea de pensar en cosas que son
consecutivas como accesibles individualmente
es una idea muy poderosa.
Porque hasta ahora solo hemos tenido esta lista de tipos de datos
bool, float, char e int.
Es una especie de lista corta de cosas muy primitivas.
Pero si quieren escribir un programa que no solo
siga pidiendo un nombre, sino que pida los nombres de dos o de diez personas
o que pida, como lo pidieron antes, el nombre o tal vez su casa
o su dormitorio o su número de teléfono o su dirección de correo electrónico,
un montón de valores diferentes,
Sería bueno poder almacenar varias cosas juntas.
Y una manera de almacenar varias cadenas es llamar a una cadena s,
llamar a la siguiente cadena t,
llamar a la siguiente cadena lo que sea, solo que venga
con nombres arbitrarios para las cadenas.
Pero eso evolucionará muy rápidamente.
Imaginen lo que el secretario de admisiones usa aquí o en Yale
para hacer un seguimiento de los estudiantes.
No tiene un programa de computadora con miles de variables dentro.
Probablemente tenga un programa de computadora para guardar
los registros de los cursos con al menos una variable llamada estudiantes.
Y dentro de esa variable de estudiantes el secretario de admisiones puede
adaptarse a un estudiante, 10 estudiantes o miles de estudiantes.
Puede crecer para completar el número de valores que realmente nos importa.
Y C no es tan poderoso como eso.
Necesitaremos otro lenguaje como Python o JavaScript para obtener dinamismo.
Pero por ahora, tenemos la capacidad de C para representar varias cosas
consecutivas, consecutivas, consecutivas, en la memoria.
Entonces no solo los caracteres en cadenas,
podemos tomar prestada esa idea de las cadenas y el almacenamiento, si queremos,
estudiante, estudiante, estudiante, estudiante como múltiples cadenas
consecutivas en vez de solamente caracteres individuales.
Y a esa idea se le llama arreglo.
Un arreglo es un fragmento contiguo de memoria,
algo consecutivo físicamente
próximo entre sí, generalmente en la RAM, que hemos presentado como hardware,
pero que no son solo caracteres, caracteres, caracteres.
Tal vez sea int, int, int, int, int o cadena, cadena, cadena, cadena,
o, más generalmente, estudiante, estudiante, estudiante, estudiante,
varias cosas consecutivas, consecutivas.
Y ahora podemos dar un vistazo
de lo que esta cosa sigue escribiendo con fe.
Int main(void) dice que los programas principales
que empezaremos a escribir para pset uno y más allá
devolverán un int, incluso si no lo hacemos nosotros mismos.
Devolverán 0 de manera predeterminada
Y veremos en breve por qué es útil para una función main
devolver un valor, a pesar de que los humanos rara vez veremos ese valor.
Pero es interesante notar que main
toma una entrada, y no la entrada en el sentido de get_int, get_string y demás.
Realmente puede darle la entrada al programa
en la llamada línea de comandos.
Todo este tiempo escribí ./mario0, ./mario1,
y no hay palabras después de eso.
Y sin embargo ya hemos mostrado clang, el compilador,
que puede tomar, por ejemplo -o y - lcs50,
esas palabras clave adicionales que de algún modo influyen en su comportamiento.
Entonces, ¿no sería bueno si pudiera escribir un programa
que no le pida su nombre al usuario?
Les dejaré escribir su nombre en la línea de comandos
y presionar Enter una vez para terminar con eso, así como clang
es solo un comando largo y se termina.
No hay indicaciones.
Bueno, podemos hacerlo si cambiamos void a esto.
Y es un trabalenguas, pero hay una versión alternativa de main
que no solo toma cero argumentos.
Eso es lo que ha significado la palabra clave void todo este tiempo.
Solo significa, main no toma ninguna entrada de modo predeterminado.
Debe avisar al usuario explícitamente con get_int o get_string o lo que sea.
Pero hay una segunda versión alternativa de main en C
que toma dos entradas
y no tiene que proporcionarlas explícitamente.
En un momento veremos cómo usarla.
Main también puede recibir dos entradas.
Una es un int, y otra un arreglo de cadenas.
El int es el número total de palabras que el humano
ha escrito en su teclado.
El argv o argumento del vector, por convención,
aunque podríamos llamarlo como queramos, eso es un arreglo de palabras
que el usuario escribió en el cursor antes de presionar Enter
y esto es útil de la siguiente manera.
A continuación, en el código fuente de hoy, abriré un ejemplo llamado
argv, por argumento del vector,
0, como sigue.
En argv0, no está pasando mucho.
Y si al menos tenemos fe en este concepto de aquí,
tal vez se pueda inferir lo que está sucediendo.
Cambié el aspecto principal de la línea seis, la firma de main,
por así decirlo,
y entonces le hago una pregunta.
If (argc == 2), entonces printf ("hello... algo.
De lo contrario, simplemente imprime el harcoded hello, world.
Parece que argv[1] está recibiendo el mismo tratamiento que le dimos
a las cadenas hace un momento.
Pero esta es la nueva sintaxis especial.
Si utilizamos corchetes como lo que utilicé,
sin números en su interior, es como si dijéramos, hey, computadora,
esta variable argv será un arreglo con algunas longitudes de cadenas.
¿Por qué cadenas?
Porque la cadena es la palabra que está inmediatamente a la izquierda, string [] argv0.
Ahora, no sé cómo van a llegar las cadenas allí,
la computadora lo hará por mí,
pero me da esta capacidad.
A continuación compilaré este programa de la siguiente forma, make argv0.
./argv0.
hello, world.
Nada interesante.
Pero si escribo mi nombre en el cursor y presiono Enter, se vuelve dinámico.
Entonces, ¿qué significa esto?
Aunque la sintaxis es un poco nueva, podemos
inferir ahora lo que debe estar haciendo.
Argc pasa por cuenta de la discusión.
Así que argc igual a 2 al parecer implica que el humano escribió dos palabras
en el cursor, el nombre del programa y cualquier cosa que haya escrito.
Mientras tanto, argv, el argumento del vector
es la variable que podemos utilizar para ir a buscar la primera o la segunda palabra
o, si hay más, la tercera y cuarta palabras.
De hecho, si cambio esto de forma manual, ¿que debería ser,
siguiendo esa lógica, en argv[0]?
PÚBLICO: [INAUDIBLE]
DAVID MALAN: Sí, el nombre del programa, ¿cierto?
Entonces, veamos.
make argv0
./argv0 David
Hello... OK.
Quiero decir, se ve algo tonto pero que es lo que estoy haciendo.
¿Podría ser atrevido y decir lo que está en la centésima ubicación de este arreglo
o lista, como también podrían verla?
make argv0
./david.
¡Huy!
Eso es malo.
Acostúmbrense a esto porque sucederá con mayor frecuencia.
El fallo de segmentación es la forma de decir que tocamos la memoria RAM,
lo cual no deberíamos hacer.
Piensen en lo que esto significa.
Si argv[0]... dejen saco mi imagen de un arreglo.
Si mi arreglo se ve así y argv[0] está aquí, y es seguro imprimirlo,
argv[1] está aquí,
es seguro imprimirlo,
es mi nombre
Y argv, que hice,
100, está como por aquí,
No sé qué hay aquí.
Y de hecho, tocar esa memoria fue muy malo.
El programa se bloqueó.
Y el fallo de segmentación alude a cómo las computadoras distribuyen la memoria.
Se tiene un segmento de memoria aquí, un segmento de memoria acá,
un segmento de memoria allá.
Fallo de segmentación significa que se tocó un fragmento de memoria
que no podíamos utilizar, cambiar o incluso ver.
Así que tuve suerte, aunque... bueno, no tuve suerte.
A veces podría ver valores basura.
Seré un poco más conservador.
Me deja poner [2], que solo es uno más de lo que he escrito.
A veces tiene un comportamiento indefinido.
No sé lo que me va a dar.
Nulo.
Así que hay algunos caracteres apestosos o ceros por allí.
Pero ahora estamos jugando con fuego, por así decirlo.
Estos son errores lógicos en mi programa.
Pero está bien para comprobar si if argc == 2, entonces
está bien observar al 0 y al 1, dos cosas y solo dos cosas.
¿Tienen alguna pregunta al respecto?
Muy bien.
Entonces, ¿dónde, en qué dominio son útiles este tipo de cosas?
Hay un par de ejemplos más de argv que pueden ver en línea.
Ocurre que en el mundo de la criptografía,
esto comienza a ponerse interesante.
Entonces, en el mundo de la criptografía se trata de codificar la información.
Tal vez en la escuela primaria
le pasaron notas a un amigo o enamorado que tenían en el aula.
Y si eran realmente inteligentes o si el maestro era conflictivo,
es posible que codificaran su mensaje, así que no solo escribían
"te amo" o lo que fuera.
En vez de ello cambiaban todas las A por B
y las B por C, o tal vez por algo más complejo
para que el maestro no pudiera cambiar las B por A y las C por B.
Pero en cierto modo revolvían las palabras.
Revolvían las palabras de tal manera
que fueran reversibles para el receptor, el receptor
de su mensaje encriptado.
Entonces, encriptar información significa convertirla a otro formato,
de lo que se llama texto plano a texto cifrado, lo cual suena genial,
y es solo la versión codificada.
Pero no es aleatorio.
Debe seguir un patrón o, si lo prefieren,
un algoritmo, así la persona que está al otro extremo puede invertir el algoritmo
y descifrarlo.
Ahora, en el ejemplo simple que propuse, A se convierte en B,
B se convierte en C. ¿Cuál es el secreto que ustedes y su enamorado conocen?
Probablemente solo es el número uno.
Esa persona debe saber que si le sumaron 1 a las letras,
entonces debe restar 1 a las letras
y con suerte sabrá que si es Z, debe regresar a la A
y no seguir una puntuación rara o algo así.
Entonces pueden tener un algoritmo tan simple como ese.
Así que podemos ver la criptografía como un ejemplo de resolución de problemas.
Desean enviar un mensaje de alguien, ustedes,
a alguien más, tal vez por medio de un método muy inseguro
como pasar una nota a través del salón,
y desean que solo una persona sepa cómo acceder a él.
Es similar a que ustedes proporcionen entradas y obtengan salidas,
su texto plano y su texto cifrado, de modo que nadie
pueda entenderlo, excepto ustedes y el destinatario.
Entonces, la criptografía
tiene diferentes formas, pero tal vez la más simple se parece a esto.
Hay dos entradas, el texto plano y el mensaje que deseamos enviar,
y luego la clave, que puede ser un número como 1, 2, 25 o 26.
Tener un número más grande que ese sería algo tonto porque
solo le daría la vuelta al alfabeto, por así decirlo.
Pero la salida será algo que se llama texto cifrado.
Y cuando sus enamorados reciban el mensaje,
solo necesitan revertir el proceso.
Deben conocer la clave,
de lo contrario, se la pasarían todo el día adivinando lo que decía
el mensaje.
Pero siempre y cuando conozcan el secreto, podrán descifrarlo.
Ahora, por supuesto que hay una trampa.
Tienen que hablar en los mismos términos que la persona a la que están enamorando,
ya que debe conocer la clave por adelantado,
de lo contrario, solo estarían enviando valores sin sentido.
Así que también es una especie de círculo vicioso.
Para enviar un mensaje secreto de A a B,
A y B deben deliberar y acordar con anticipación dicho secreto.
Pero si necesitan ponerse de acuerdo por adelantado,
¿por qué no utilizan ese tiempo para dar el mensaje directamente a la persona?
¿Verdad?
Entonces hay una desconexión.
Y volveremos a esto muy pronto porque la mayoría de nosotros
probablemente no conoce a alguien que trabaja en amazon.com.
Pero, cuando compro algo en Amazon,
todos estos años me han dicho que es seguro,
que está encriptado.
Mi tarjeta de crédito, mi nombre y todo eso, de alguna manera están
encriptados entre Amazon y yo, en Seattle o donde sea que estén sus servidores.
Pero no conozco a nadie allí.
Y sin embargo, de alguna manera, la criptografía también funciona.
Este tipo de cartografía es conocida como criptografía de clave secreta.
Pero también hay criptografía de clave pública y otras más.
Y entonces, lo que encontrarán de particular en la serie de problemas dos
es que tendrán la oportunidad de explorar este mundo
en el cual escribirán un software que encripte y luego, con suerte,
descifra la información e incluso, si usted está entre los más cómodos,
una oportunidad para tratar de escribir software
que maneje contraseñas cifradas,
o dicho más precisamente, encriptadas.
Más aún, tarde o temprano,
tratarán de romper las contraseñas, es decir, descifrar
cuáles eran las contraseñas.
Y al final todo se reduce, en el contexto de C,
a tomar un mensaje como entrada, un texto plano,
y de alguna manera convertirlo en texto cifrado, manipulando
sus caracteres individuales, o a la inversa, si eres el destinatario.
Y me gustaría mostrar un clip de una película que pueden ver
cada hora en punto durante la época decembrina, "Una historia
de Navidad", porque contiene un ejemplo sencillo sobre la criptografía.
Si nunca han visto esta película, él es el pequeño Ralphie,
y está muy emocionado porque durante meses o algo así
junta y envía todas esas como...
etiquetas que vienen en las cajas de cereales, o algo así,
y consigue por fin este anillo decodificador secreto.
Y el anillo decodificador secreto es una especie de simpático modelo mental
para ilustrar el tipo de criptografía que estoy proponiendo aquí,
este tipo de idea sobre la rotación,
A se convierte B, B se convierte en C. Porque si se imaginan
un anillo que tiene otro anillo en el exterior,
pueden alinear las A y las Z, por así decirlo, de manera diferente.
Y eso es para lo que estaba ahorrando.
Así que pensé que debíamos dedicar un momento para ver este clip
e inspirar uno de los problemas que tenemos por delante.
[REPRODUCCIÓN DE VIDEO]
-Sea conocido por todos que Ralphie Parker
ha sido nombrado miembro del Club Secreto de Anita la Huerfanita
y goza de todos sus honores y beneficios.
también
-¡Firma: Anita la Huerfanita!
¡Contrafirma: Pierre André!
¡En tinta!
¡Honores y beneficios a los nueve años de edad!
-¡No exageremos!
-Vamos,
continuemos con el programa.
No necesito todo eso de contrabandistas y piratas.
-Escuchen mañana por la noche la aventura final de
"El barco pirata negro".
Ha llegado la hora del mensaje secreto para los miembros del club.
Recuerden que solo los miembros del club secreto
pueden descifrar el mensaje secreto de Anita.
Recuerden que Anita cuenta con ustedes.
Coloquen sus plumas en la clave B2.
He aquí el mensaje.
12... 11...
-Estoy dentro.
Es mi primera reunión secreta.
...14... 11... 18... 16...
-Oh, Pierre estuvo muy bien anoche.
Yo sabía que el mensaje iba a ser importante.
...3... 25...
Es un mensaje de la propia Anita.
Recuerden, no se lo digan a nadie.
90 segundos después me traslade al único cuarto donde un niño de nueve años
podía sentarse y descifrar a solas.
¡Ajá!
¡B!
Pasé al siguiente.
¡E! ¡La primera palabra es "be"!
S, cada vez era más fácil.
U.
-¡Oh, vamos, Ralphie!
¡Tengo que irme!
-¡Salgo enseguida, mamá!
Recórcholis.
-¡T, O!
"No olvides"... ¿No olvides qué?
¿Qué trataba de decir Anita?
"No olvides", ¿qué?
-Ralphie, Randy tiene que irse.
¿Quieres salir, por favor?
-¡Sí, mamá!
¡Ya voy!
-Ya estaba cerca
Qué terrible tensión.
¿Qué era eso?
El destino del planeta podía estar en juego.
[TOQUIDOS]
-¡Ralph! ¡Randy tiene que irse!
-¡Saldría para gritarlo!
DAVID: ¡Ya casi! ¡Casi lo tenía!
Mis dedos volaron.
Mi mente era una trampa de acero.
Cada poro vibraba.
Era casi claro.
¡Sí!
-"Asegúrate de tomar tu chocolate Ovamaltina"
¿Ovamaltina?
¿Es un miserable comercial?
¡Hijos de p...!
[FINALIZA LA REPRODUCCIÓN]
DAVID: Esto fue CS50.
¡Hasta la próxima!
[APLAUSOS]