Apuntes · Capítulo 1Fundamentos
Qué es computar
Qué es computar
Este es el primer capítulo del curso y no supone nada: ni que hayan programado antes, ni que hayan abierto una terminal. Al terminar la semana van a poder hacer tres cosas. Describir un procedimiento con tanta precisión que hasta una máquina pueda seguirlo. Comprobarlo a mano, con papel y lápiz. Y escribir su primer programa en C, compilarlo y ejecutarlo.
La pregunta de fondo es qué significa computar. La respuesta corta: seguir pasos, uno tras otro, sin entender lo que se hace y sin agregar nada de cosecha propia. Una computadora hace solo eso, muy rápido. Todo lo que sabe hacer alguien tuvo que explicárselo antes, sin dejar ningún detalle librado a su criterio. De eso trata este curso.
Lectura. Estos apuntes son el texto del curso: no hay libro ni lecturas externas. Los programas del capítulo están en
code/week-01. Más adelante, en este mismo capítulo, verán cómo ejecutarlos y cómo correr sus pruebas conmake test.
Ruta de lectura.
- Antes de clase (lectura obligatoria, 15 a 20 minutos): la introducción, “Qué es un algoritmo”, “Un primer problema: dar vuelto” y “Pseudocódigo”. Es lo mínimo para seguir la clase.
- En clase: “El curso en una página”, “Seguir un algoritmo a mano”, “Diagramas de flujo”, “De las ideas a la máquina: niveles de lenguaje”, “Del código fuente al ejecutable”, “Anatomía de
hola.c” y “Cuando algo sale mal”.- Después de clase: para el laboratorio, “La terminal desde cero” y “El laboratorio, paso a paso”; para repasar, “Resumen” y “Ejercicios”. “El código de la semana” explica cómo correr los ejemplos por su cuenta.
El curso en una página
El curso tiene dos sesiones por semana: teoría (2 horas) y laboratorio (2 horas, práctica guiada). Se divide en cuatro partes:
| Parte | Semanas | De qué trata |
|---|---|---|
| Fundamentos | 1 a 3 | Algoritmos, pseudocódigo, variables, operadores, bits, terminal y Git |
| Programación en C | 4 a 7 | Decisiones, ciclos, números aleatorios, funciones y pruebas |
| Memoria y datos | 9 a 13 | Cómo funciona una computadora, arreglos, cadenas, matrices, struct, archivos, búsqueda y eficiencia |
| Computación moderna | 14 y 15 | Una mirada a Python, internet e inteligencia artificial |
Cada algoritmo se piensa primero en pseudocódigo y después se escribe en C. Elegimos C porque obliga a entender qué pasa en la memoria de la computadora. No es el lenguaje más cómodo para empezar, pero lo que aprendan con él les va a servir en cualquier otro.
Hay un examen parcial en la semana 8, un examen final en la semana 16 y cuatro prácticas, en las semanas 4, 7, 11 y 14. Desde hoy van a practicar seguir programas a mano. El trabajo es individual: se pueden discutir ideas, pero cada uno escribe su propio código y debe poder explicarlo. El calendario completo y todas las reglas están en el programa del curso.
Qué es un algoritmo
Un algoritmo (algorithm) es una secuencia finita de pasos precisos que resuelve un problema. La palabra viene del nombre de al-Juarismi, un matemático persa del siglo IX.
Ya conocen muchos algoritmos sin haberlos llamado así. Sumar dos números de varias cifras, llevando lo que sobra a la columna siguiente, es uno: si siguen las reglas, el resultado sale bien aunque estén distraídos, y nadie tiene que “inspirarse”. Esa es la idea central. Quien sigue un algoritmo no necesita entender el problema. Solo necesita saber hacer cada paso.
Para que algo sea un algoritmo, debe cumplir tres condiciones:
| Condición | Qué significa | Ejemplo que la rompe |
|---|---|---|
| Preciso | Cada paso dice exactamente qué hacer | “Agrega sal al gusto” |
| Sin ambigüedad | Dos personas que lo sigan hacen lo mismo | “Dobla por la esquina de siempre” |
| Finito | Termina después de un número finito de pasos | “Lava, enjuaga, repite” |
El tercer ejemplo es la instrucción de un frasco de champú. Tomada al pie de la letra, nunca termina: no dice cuántas veces repetir. Una persona lo resuelve sin pensarlo. Una computadora se quedaría lavando para siempre.
Los algoritmos, además, trabajan con datos. Reciben una entrada (el problema concreto) y producen una salida (la respuesta). Un mismo algoritmo sirve para muchas entradas. “Sumar 17 + 25” es un cálculo. “Cómo sumar dos números cualesquiera” es un algoritmo.
Error común. Escribir pasos que una persona entiende por contexto, pero que no dicen exactamente qué hacer. “Dale su vuelto” funciona con un cajero porque el cajero ya sabe cómo se hace. No le dice nada a quien no lo sabe. Para probar un algoritmo, léanlo como si fueran una máquina muy obediente y sin sentido común: si en algún paso tienen que adivinar, ese paso está mal escrito.
Un primer problema: dar vuelto
Trabajan en una bodega. Un cliente debe recibir S/ 7.80 de vuelto y quieren darle la menor cantidad posible de monedas. Para este problema suponemos que hay monedas ilimitadas de estos valores: S/ 5, S/ 2, S/ 1, S/ 0.50, S/ 0.20 y S/ 0.10.
Una persona lo resuelve así: entrega la moneda más grande que no se pase del monto, ve cuánto falta y repite.
- Faltan S/ 7.80. La más grande que cabe es S/ 5. Quedan S/ 2.80.
- Faltan S/ 2.80. La más grande que cabe es S/ 2. Quedan S/ 0.80.
- Faltan S/ 0.80. La de S/ 1 ya no cabe. La más grande que cabe es S/ 0.50. Quedan S/ 0.30.
- Faltan S/ 0.30. La más grande que cabe es S/ 0.20. Quedan S/ 0.10.
- Faltan S/ 0.10. Cabe la de S/ 0.10. No queda nada.
Son cinco monedas. Ahora hay que escribir esta idea de modo que no dependa de quien la lee. Para eso sirve el pseudocódigo.
Antes, una decisión práctica: vamos a contar en céntimos, no en soles. S/ 7.80 son 780 céntimos y las monedas valen 500, 200, 100, 50, 20 y 10. Así todos los números son enteros y las cuentas son exactas. En la semana 3 verán por qué las computadoras tienen problemas con números como 0.10.
Pseudocódigo
El pseudocódigo (pseudocode) es una forma de escribir un algoritmo sin las reglas de un lenguaje real. No lo ejecuta ninguna máquina: lo leen las personas. Sirve para pensar el algoritmo antes de pelear con el lenguaje de programación.
En este curso siempre lo escribimos con el mismo formato. Este es el algoritmo del vuelto completo:
algoritmo dar_vuelto(vuelto)
monedas ← 0
mientras vuelto ≥ 500 hacer
escribir 500
vuelto ← vuelto - 500
monedas ← monedas + 1
fin mientras
mientras vuelto ≥ 200 hacer
escribir 200
vuelto ← vuelto - 200
monedas ← monedas + 1
fin mientras
mientras vuelto ≥ 100 hacer
escribir 100
vuelto ← vuelto - 100
monedas ← monedas + 1
fin mientras
mientras vuelto ≥ 50 hacer
escribir 50
vuelto ← vuelto - 50
monedas ← monedas + 1
fin mientras
mientras vuelto ≥ 20 hacer
escribir 20
vuelto ← vuelto - 20
monedas ← monedas + 1
fin mientras
mientras vuelto ≥ 10 hacer
escribir 10
vuelto ← vuelto - 10
monedas ← monedas + 1
fin mientras
escribir monedas
fin algoritmo
Es largo, pero es el mismo bloque seis veces, uno por cada moneda. Veamos las piezas.
El nombre y los datos. algoritmo dar_vuelto(vuelto) le da un nombre al
algoritmo y dice qué dato recibe: el vuelto en céntimos. fin algoritmo marca
dónde termina.
Las variables. vuelto y monedas son variables. Una variable es un
nombre para un lugar donde se guarda un valor. Imagínenla como una caja con una
etiqueta: adentro hay un solo valor a la vez y ese valor puede cambiar. En la
semana 2 las verán en C.
La asignación. La flecha ← guarda un valor en una variable. monedas ← 0 se
lee “monedas recibe 0”. Primero se calcula lo que está a la derecha y luego se
guarda a la izquierda. Por eso monedas ← monedas + 1 tiene sentido: toma el valor
actual, le suma 1 y guarda el resultado en la misma caja. El valor anterior se
pierde.
Idea clave.
←no es una igualdad matemática. Como ecuación,monedas = monedas + 1no tendría solución. Como instrucción es perfectamente clara: “aumentamonedasen 1”.
Escribir. escribir 500 muestra el valor al usuario. Aquí significa “entrega
una moneda de 500 céntimos”. Existe también leer, que pide un dato al usuario.
Repetir. mientras condición hacer ... fin mientras funciona así:
- Se pregunta si la condición es cierta.
- Si es cierta, se ejecuta lo que está adentro, una vez, y se vuelve al punto 1.
- Si es falsa, se salta a lo que sigue después de
fin mientras.
La condición puede ser falsa desde el inicio. En ese caso, lo de adentro no se ejecuta ni una vez.
Decidir. El vuelto no lo necesita, pero otra pieza básica es si. Este
algoritmo recibe dos números y escribe el mayor:
algoritmo mayor_de_dos(a, b)
si a > b entonces
escribir a
sino
escribir b
fin si
fin algoritmo
Se ejecuta una sola de las dos ramas. Si los números son iguales, a > b es
falso y se escribe b, que da lo mismo.
Las reglas del formato son pocas:
| Regla | Detalle |
|---|---|
| Palabras clave | En español y minúsculas: algoritmo, si, entonces, sino, mientras, hacer, para, desde, hasta, devolver, leer, escribir |
| Asignar | Con ← |
| Comparar | Con =, ≠, <, >, ≤, ≥ |
| Bloques | Lo que está adentro lleva una sangría de 4 espacios, y el bloque se cierra con fin si, fin mientras, fin para o fin algoritmo |
Las palabras para y devolver aparecerán más adelante, junto con los ciclos y
las funciones.
Seguir un algoritmo a mano
Seguir un algoritmo (también se dice hacer su traza) es ejecutarlo ustedes mismos, paso a paso, anotando cómo cambian las variables. Es la herramienta más importante del curso. Sirve para entender un algoritmo ajeno, para encontrar errores en el propio y para responder preguntas de examen.
La técnica es una tabla con una columna por variable. Cada vez que algo cambia, se
anota. Sigamos dar_vuelto con vuelto = 780:
| Paso | Qué se ejecuta | ¿Cierto? | vuelto |
monedas |
Escribe |
|---|---|---|---|---|---|
| 0 | Inicio: monedas ← 0 |
780 | 0 | ||
| 1 | ¿780 ≥ 500? | sí | 280 | 1 | 500 |
| 2 | ¿280 ≥ 500? | no | 280 | 1 | |
| 3 | ¿280 ≥ 200? | sí | 80 | 2 | 200 |
| 4 | ¿80 ≥ 200? | no | 80 | 2 | |
| 5 | ¿80 ≥ 100? | no | 80 | 2 | |
| 6 | ¿80 ≥ 50? | sí | 30 | 3 | 50 |
| 7 | ¿30 ≥ 50? | no | 30 | 3 | |
| 8 | ¿30 ≥ 20? | sí | 10 | 4 | 20 |
| 9 | ¿10 ≥ 20? | no | 10 | 4 | |
| 10 | ¿10 ≥ 10? | sí | 0 | 5 | 10 |
| 11 | ¿0 ≥ 10? | no | 0 | 5 | |
| 12 | escribir monedas |
0 | 5 | 5 |
Las monedas son 500, 200, 50, 20 y 10. Suman 780, son cinco y vuelto terminó en
0. Coincide con lo que hicimos de cabeza.
Fíjense en dos detalles de la tabla. En el paso 5 el bloque de la moneda de 100 se salta completo, porque su condición es falsa desde el inicio. Y en los pasos 2, 4, 7, 9 y 11 hay que hacer la pregunta de nuevo aunque ya se sepa la respuesta. La máquina no sabe: pregunta.
Sigamos también mayor_de_dos con dos notas, a = 14 y b = 17:
| Paso | Qué se ejecuta | ¿Cierto? | Escribe |
|---|---|---|---|
| 1 | ¿14 > 17? | no | |
| 2 | Rama sino: escribir b |
17 |
Error común. Olvidar la línea que cambia la condición de un
mientras. Sinvuelto ← vuelto - 500,vueltose queda en 780, la pregunta ¿780 ≥ 500? siempre responde sí y el algoritmo no termina. Es un ciclo infinito. Ya no es un algoritmo, porque no es finito. Cuando escriban unmientras, pregúntense qué línea de adentro acerca la condición a ser falsa.
Antes de confiar: los datos válidos
¿Qué hace dar_vuelto con 785? Sigan la tabla: salen las mismas monedas que con
780, pero vuelto termina en 5, porque ninguna moneda cabe en 5 céntimos. El
algoritmo termina, pero no entregó todo el vuelto, y nadie le avisó al usuario.
Esto no es un defecto de la tabla. El algoritmo supone algo sobre su dato: que es un múltiplo de 10 y que no es negativo. Conviene decirlo por escrito. Parte de definir un algoritmo es definir para qué datos funciona.
¿De verdad da la menor cantidad?
Que un método parezca razonable no significa que sea correcto. La idea de tomar siempre la moneda más grande que cabe no funciona con todos los juegos de monedas.
Imaginen un país con monedas de 1, 3 y 4, y un vuelto de 6:
| Método | Monedas | Cantidad |
|---|---|---|
| La más grande que cabe | 4, 1, 1 | 3 |
| La mejor posible | 3, 3 | 2 |
Con esas monedas, el método da un vuelto correcto, pero no el más corto. El problema pedía la menor cantidad, así que ahí el algoritmo no lo resuelve.
Con los valores que elegimos para los soles sí funciona. No lo demostramos en este
curso, pero sí lo comprobamos: el archivo test_algorithms.py de la carpeta de la
semana calcula, para cada monto de 10 en 10 céntimos hasta S/ 100, la menor cantidad
posible probando todas las combinaciones, y la compara con lo que da el método. Son
iguales en todos los casos. Eso es una comprobación para esos montos, no una
demostración para todos.
Idea clave. Un ejemplo que sale bien no prueba que un algoritmo sea correcto. Un ejemplo que sale mal sí prueba que no lo es.
Diagramas de flujo
Un diagrama de flujo (flowchart) dibuja el mismo algoritmo con formas conectadas por flechas. El camino que siguen las flechas muestra el orden en que se ejecutan los pasos. Es útil para ver de un vistazo dónde se decide y dónde se repite.
Cada forma tiene un significado:
| Forma | Significado | Equivale a |
|---|---|---|
| Óvalo | Inicio o fin | algoritmo, fin algoritmo |
| Paralelogramo | Entrada o salida de datos | leer, escribir |
| Rectángulo | Una acción o cálculo | monedas ← 0 |
| Rombo | Una pregunta con dos salidas, sí y no | si, mientras |
| Flecha | Qué paso sigue | El orden de las líneas |
Tomemos solo el primer bloque del vuelto: contar cuántas monedas de S/ 5 se pueden entregar.
algoritmo monedas_de_cinco(vuelto)
monedas ← 0
mientras vuelto ≥ 500 hacer
vuelto ← vuelto - 500
monedas ← monedas + 1
fin mientras
escribir monedas
fin algoritmo
Este es su diagrama. Cada forma lleva un número para poder nombrarla:
La flecha que va de 6 a 4, hacia atrás, es el ciclo: en el dibujo se ve como una
vuelta. Un si, en cambio, tiene dos caminos que más abajo se juntan, sin ninguna
flecha que regrese.
Si siguen este diagrama con vuelto = 1300, pasan dos veces por las formas 5 y 6,
vuelto termina en 300 y escribe 2. Los diagramas sirven bien para algoritmos cortos. Cuando crecen, se
vuelven difíciles de dibujar y de modificar, y por eso en el curso usaremos sobre
todo pseudocódigo.
De las ideas a la máquina: niveles de lenguaje
La computadora no entiende pseudocódigo. Tampoco entiende C. Su procesador, la CPU, solo ejecuta código máquina: instrucciones muy simples guardadas como números. Cada familia de procesadores tiene el suyo.
Un lenguaje de bajo nivel está cerca de esas instrucciones. Un lenguaje de alto nivel está cerca de cómo piensan las personas, y una sola línea suya equivale a varias instrucciones de la máquina. El nivel es relativo: C es de alto nivel comparado con el código máquina, y de más bajo nivel que Python.
| Nivel | Ejemplo | Quién lo usa |
|---|---|---|
| Pseudocódigo | escribir "Hola, mundo" |
Personas, para pensar |
| Alto nivel (C) | printf("Hola, mundo\n"); |
Personas, para programar |
| Bajo nivel (ensamblador) | bl puts |
Personas, en casos especiales |
| Código máquina | Números | La CPU |
El ensamblador es una forma legible del código máquina: una línea por
instrucción, con nombres cortos en vez de números. Más adelante van a ver cómo
pedirle a gcc que lo muestre. No tienen que aprenderlo en este curso.
Para pasar de C a código máquina se usa un compilador (compiler): un programa que traduce
el archivo que escribimos, el código fuente, a un ejecutable, el archivo que
la computadora sí puede correr. Nuestro compilador se llama gcc.
Para usar gcc hay que aprender una herramienta nueva: la terminal.
La terminal desde cero
Hasta hoy han usado la computadora con ventanas, íconos y el mouse. Hay otra forma: escribir órdenes. Al principio parece más difícil, pero tiene ventajas. Una orden escrita se puede repetir, copiar y guardar. Además, es la forma normal de usar el compilador, Git y los servidores, así que la van a usar toda su carrera.
Terminal, shell, prompt
Tres palabras que se confunden:
| Palabra | Qué es |
|---|---|
| Terminal | La ventana donde se escribe y se leen las respuestas |
| Shell | El programa que lee lo que escriben y lo ejecuta. El nuestro se llama bash |
| Prompt | El texto que el shell muestra cuando está listo para recibir una orden |
Al abrir la terminal del entorno del curso verán algo parecido a esto:
root@616988be0ce2:/home/ana#
Eso es el prompt. No se escribe: lo pone el shell. Aquí dice tres cosas: el usuario
(root, el administrador dentro de este entorno), el nombre de la máquina
(616988be0ce2) y la carpeta donde están parados (/home/ana). El de ustedes va a
ser distinto, porque depende de la máquina y de la carpeta. En los ejemplos de este
capítulo usamos /home/ana como carpeta personal de una estudiante; en el entorno del
curso, la carpeta personal es otra.
Como cada prompt es distinto, en este curso lo escribimos siempre como un simple
$. Cuando vean esto:
$ pwd
/home/ana
significa: escriban pwd (sin el $), presionen Enter, y la computadora
responde /home/ana. Las líneas que empiezan con $ las escriben ustedes. Las demás
son la respuesta.
Idea clave. La terminal no hace nada hasta que presionan Enter. Y cuando un comando termina, vuelve a aparecer el prompt. Si el prompt no vuelve, el programa sigue corriendo o está esperando que escriban algo.
Comandos y argumentos
Una orden se llama comando. Tiene un nombre y, a veces, datos adicionales separados por espacios:
$ mkdir lab01
mkdir es el comando y lab01 es su argumento. Algunos argumentos empiezan con
un guion y cambian el comportamiento del comando: se llaman opciones. Por
ejemplo, ls lista archivos, y ls -l los lista con más detalle.
El espacio separa argumentos, así que importa. mkdir lab01 y mkdirlab01 no son lo
mismo. Las mayúsculas también importan: el comando es ls, no Ls.
$ Ls
bash: Ls: command not found
Command not found significa “comando no encontrado”. Los mensajes vienen en inglés, y van a aprender a leerlos sin miedo, porque son lo que más ayuda.
Carpetas y rutas
Los archivos de la computadora se guardan en carpetas, y las carpetas pueden
contener otras carpetas. Forman un árbol. El entorno del curso usa Linux, un sistema
operativo como Windows o macOS. En Linux, todo cuelga de una carpeta raíz, que se
escribe /.
El comando tree dibuja ese árbol. Este es el de una estudiante, Ana, que tiene una
carpeta lab01 con un archivo adentro:
$ tree
.
└── lab01
└── hola.c
2 directories, 1 file
La carpeta de Ana está en /home/ana. Es decir: dentro de la raíz / está home,
dentro de home está ana. Ese texto se llama ruta (path), y es la dirección de
un archivo o carpeta.
Hay dos tipos de rutas:
- Una ruta absoluta empieza con
/y se lee desde la raíz:/home/ana/lab01/hola.c. Funciona desde cualquier lugar. - Una ruta relativa no empieza con
/y se lee desde la carpeta donde están parados: si están en/home/ana, entonceslab01/hola.ces el mismo archivo.
En todo momento, el shell tiene una carpeta actual: la carpeta donde están parados. Y hay tres nombres especiales:
| Nombre | Significa |
|---|---|
. |
La carpeta actual |
.. |
La carpeta que contiene a la actual (la “de arriba”) |
~ |
Su carpeta personal |
Los cinco comandos de hoy
| Comando | Viene de | Qué hace |
|---|---|---|
pwd |
print working directory | Escribe la ruta de la carpeta actual |
ls |
list | Lista lo que hay en la carpeta actual |
cd ruta |
change directory | Cambia la carpeta actual |
mkdir nombre |
make directory | Crea una carpeta |
cat archivo |
concatenate | Muestra el contenido de un archivo |
Veámoslos en acción. Ana empieza en su carpeta personal, todavía vacía. Las rutas de ustedes serán otras, pero los comandos funcionan igual.
$ pwd
/home/ana
$ ls
$ mkdir lab01
$ ls
lab01
$ cd lab01
$ pwd
/home/ana/lab01
$ ls
$ cd ..
$ pwd
/home/ana
Sigamos la sesión:
| Comando | Qué pasó |
|---|---|
pwd |
Estamos en /home/ana |
ls |
No escribe nada: la carpeta está vacía |
mkdir lab01 |
No escribe nada: funcionó |
ls |
Ahora aparece lab01 |
cd lab01 |
Entramos. No escribe nada, pero la carpeta actual cambió |
pwd |
Lo confirma |
ls |
lab01 está vacía |
cd .. |
Subimos un nivel |
pwd |
Volvimos a /home/ana |
Idea clave. En la terminal, el silencio es una buena noticia. Si un comando funciona y no tiene nada que mostrar, no dice nada. Si falla, se queja.
Los .. se pueden encadenar con /. Estando en /home/ana/lab01, cd ../.. sube
dos niveles, y cd ~ regresa a la carpeta personal desde donde sea. La carpeta personal
de Ana es /home/ana:
$ cd ../..
$ pwd
/home
$ ls
ana
$ cd ~
$ pwd
/home/ana
Equivocarse en la terminal
Equivocarse en la terminal no rompe nada, y el mensaje suele decir qué pasó. Si escriben mal el nombre de una carpeta:
$ cd labo1
bash: cd: labo1: No such file or directory
No such file or directory significa “no existe ese archivo o carpeta”. Revisen con
ls qué hay y con pwd dónde están. Casi siempre, o el nombre está mal escrito, o
están en otra carpeta.
Tampoco conviene usar espacios en los nombres. Como el espacio separa argumentos,
cd recibe aquí dos argumentos, mi y carpeta:
$ cd mi carpeta
bash: cd: too many arguments
Por eso en este curso los nombres van sin espacios ni tildes: lab01, change_780.c.
Error común. Usar
cd ~para volver a la carpeta del curso. En el entorno del curso, el usuario esrooty su carpeta personal es/root, que no tiene nada del curso. Para volver, escriban la ruta absoluta de la carpeta del curso. Anótenla la primera vez que abran el entorno: es lo que respondepwden ese momento.
Error común. Tratar
Hola.cyhola.ccomo si fueran el mismo archivo. En Linux son dos archivos distintos. Si usan Docker en una laptop con Windows o macOS, la carpeta compartida a veces no los distingue, y un programa que funciona allí falla en otro lado. Escriban siempre los nombres exactos.
Tres ayudas que ahorran tiempo
- Tab completa. Escriban
cd lay presionen la tecla Tab: el shell completalab01si no hay otra opción. Úsenla siempre: escriben menos y no se equivocan en los nombres. - Las flechas recuerdan. La flecha hacia arriba trae el comando anterior. Pueden editarlo y volver a ejecutarlo.
- Ctrl+C corta. Si un programa se quedó colgado, mantengan Ctrl y presionen
C. El prompt vuelve. Si está esperando datos que no piensan escribir, también
sirve Ctrl+D, que indica “no hay más datos”. Prueben
catsin argumentos: se queda esperando, y no es que se haya malogrado.
El comando clear limpia la pantalla. Y rm archivo borra un archivo: úsenlo con
cuidado, porque la terminal no tiene papelera. Lo borrado no se recupera.
Escribir un archivo
Para programar necesitan un editor de texto, que guarda letras tal cual, sin formato. Word no sirve: guarda mucho más que las letras. Tienen dos caminos, y los dos dejan el mismo archivo.
El editor del entorno. En Codespaces, el panel de arriba es un editor. Pueden abrir un archivo desde la lista de la izquierda o escribiendo en la terminal:
$ code hola.c
Con Docker en su laptop, el entorno corre en un contenedor, una especie de computadora aislada dentro de la suya. Usen el editor que prefieran en la laptop, porque la carpeta es la misma dentro y fuera del contenedor. En ambos casos, guarden con Ctrl+S (o Cmd+S en Mac).
nano, dentro de la terminal. Funciona en cualquier máquina, también en
servidores sin ventanas:
$ nano hola.c
Se abre el editor en la misma terminal. Escriban el texto y usen las teclas que
aparecen abajo, donde ^ significa Ctrl:
| Teclas | Qué hace |
|---|---|
| Ctrl+O y luego Enter | Guarda |
| Ctrl+X | Sale |
Error común. Cambiar el archivo y no guardarlo. Para
gccsolo existe lo que está guardado en el disco. Si corrigieron algo y el error sigue igual, revisen concat hola.cqué tiene en realidad el archivo.
Del código fuente al ejecutable
Ahora sí. Este es el programa más pequeño que hace algo visible, y el primero que
van a escribir. Es el archivo hola.c:
#include <stdio.h>
int main(void) {
printf("Hola, mundo\n");
return 0;
}
Después de guardarlo, Ana revisa que esté y lo muestra con cat:
$ ls
hola.c
$ cat hola.c
#include <stdio.h>
int main(void) {
printf("Hola, mundo\n");
return 0;
}
Si intenta ejecutarlo todavía no hay nada que ejecutar:
$ ./hola
bash: ./hola: No such file or directory
Hay que compilar:
$ gcc -Wall -Wextra -std=c17 hola.c -o hola
$ ls
hola hola.c
$ ./hola
Hola, mundo
gcc no dijo nada: buena noticia. Y ahora hay un archivo nuevo, hola. Al ejecutarlo
aparece el saludo. Acaban de ver el ciclo básico de programar en C: escribir,
compilar, ejecutar.
El comando de compilación tiene varias partes, y conviene entender cada una:
| Parte | Qué significa |
|---|---|
gcc |
El compilador |
-Wall -Wextra |
Pide que avise de muchas cosas sospechosas (W de warning, advertencia) |
-std=c17 |
Usa la versión del lenguaje que se llama C17, la del curso |
hola.c |
El archivo a traducir |
-o hola |
El nombre del ejecutable que debe crear (o de output, salida) |
Si olvidan -o hola, gcc no da error: crea un ejecutable con el nombre a.out.
¿Por qué ./hola y no hola?
$ hola
bash: hola: command not found
Cuando escriben una palabra sola, el shell busca un comando con ese nombre, pero solo
en una lista fija de carpetas del sistema, donde viven ls, gcc y los demás. La
carpeta actual no está en esa lista, así que no lo encuentra aunque el archivo esté
ahí mismo. Si escriben una ruta, el shell va directo a ese archivo. ./hola es la
ruta relativa de “el archivo hola que está aquí”, porque . es la carpeta actual.
Por la misma razón, el laboratorio les pide correr env/check.sh: es una ruta, la
del archivo check.sh dentro de la carpeta env.
¿Qué archivo ejecuta la computadora?
hola.c es texto para personas. La computadora ejecuta hola. Si intentan ejecutar
el código fuente, el shell se niega:
$ ./hola.c
bash: ./hola.c: Permission denied
Permission denied es “permiso denegado”. Los archivos de texto no tienen el permiso
de ejecución, y aunque lo tuvieran, la CPU no entiende C. ls -l muestra la diferencia:
$ ls -l
-rwxr-xr-x 1 root root 70432 Nov 2 07:51 hola
-rw-r--r-- 1 root root 82 Nov 2 07:50 hola.c
La x de la primera columna indica que hola se puede ejecutar. El número antes de
la fecha es el tamaño en bytes (un byte es la unidad de tamaño de los archivos; la
verán en la semana 3). hola.c ocupa 82 bytes. hola ocupa bastante más, porque
tiene mucho más que las instrucciones de nuestras pocas líneas. Ese tamaño, y las
fechas, cambian de una máquina a otra.
El comando file dice qué tipo de archivo es:
$ file hola
hola: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=5c8baa6788f42a3cd12a18d55af119af9033f3ee, for GNU/Linux 3.7.0, not stripped
No hace falta entenderlo todo. Executable dice que es un ejecutable. ARM aarch64
es el tipo de procesador para el que se compiló esa vez, y en su máquina puede decir
otro, como x86-64. Ese es el punto: un ejecutable sirve para una familia de
procesadores y un sistema operativo. El código fuente se puede volver a compilar en
otra máquina; el ejecutable, en general, no se puede simplemente copiar a cualquier
máquina.
Idea clave.
hola.ces lo que ustedes escriben y conservan.holaes lo que la computadora ejecuta, y se puede volver a generar en cualquier momento. Si cambianhola.c,holano se entera: hay que compilar de nuevo.
Qué hace gcc por dentro
“Compilar” es, en realidad, una cadena de cuatro pasos. gcc los hace todos con un
solo comando:
| Paso | Herramienta | Qué hace |
|---|---|---|
| 1 | Preprocesador | Atiende las líneas con #. Con #include copia el contenido de stdio.h |
| 2 | Compilador | Traduce C a ensamblador y revisa las reglas del lenguaje |
| 3 | Ensamblador | Traduce el ensamblador a código máquina |
| 4 | Enlazador (linker) | Conecta ese código máquina con la biblioteca de C, una colección de funciones ya escritas, como printf, y crea el ejecutable |
Se puede detener la cadena tras el paso 2 con la opción -S, para mirar el
ensamblador:
$ gcc -S hola.c -o hola.s
El archivo hola.s tiene muchas líneas auxiliares que empiezan con punto. Estas son
las de la función main, sin esas líneas, tal como salieron en la máquina donde
preparamos estos apuntes. Son de un procesador ARM. En una máquina x86-64 serán
otras:
main:
stp x29, x30, [sp, -16]!
mov x29, sp
adrp x0, .LC0
add x0, x0, :lo12:.LC0
bl puts
mov w0, 0
ldp x29, x30, [sp], 16
ret
No traten de entenderlas. Solo noten cuánto trabajo le ahorra C: nuestras dos líneas
se volvieron ocho instrucciones, y ninguna habla de saludos. También noten que gcc no llamó a
printf, sino a puts, otra función de C. Como el texto es fijo y termina en salto de
línea, decidió que era equivalente. Un compilador puede cambiar cómo se hace algo si
el resultado del programa es el mismo.
Anatomía de hola.c
Línea por línea:
#include <stdio.h>
#include le pide al preprocesador que incluya otro archivo. stdio.h (standard
input/output, entrada y salida estándar) es parte de C y le informa al compilador que
existe printf y cómo se usa. El código de printf no está ahí: está en la biblioteca
de C. El enlazador deja anotado que el programa la necesita, y el sistema la carga cuando
el programa empieza a correr. Eso es lo que quiere decir dynamically linked en la
salida de file.
int main(void) {
Esto define una función llamada main. Una función es un bloque de código con
nombre (la semana 7 trata de ellas). Todo programa en C empieza a ejecutarse en main,
así que ese nombre es obligatorio. int dice que devuelve un número entero, y void
dice que no recibe datos. La llave { abre el cuerpo de la función.
printf("Hola, mundo\n");
printf escribe texto en la terminal. Lo que está entre comillas se llama cadena:
un texto para el programa, no una orden. \n representa un salto de línea: se
escribe con dos caracteres, pero en la salida es uno solo, y hace que lo siguiente
empiece en la línea de abajo. El ; termina la instrucción. En C, cada instrucción
termina en punto y coma.
return 0;
return termina main y le entrega un número a quien ejecutó el programa, en este
caso el shell. Por convención, 0 significa “todo salió bien” y cualquier otro valor
indica algún problema. Se puede ver con echo $?, que escribe el resultado del último
comando:
$ ./hola
Hola, mundo
$ echo $?
0
El programa exit_code.c termina con return 7:
$ gcc -Wall -Wextra -std=c17 exit_code.c -o exit_code
$ ./exit_code
Terminó con el código 7
$ echo $?
7
}
La llave cierra el cuerpo de main.
Las instrucciones se ejecutan en orden, de arriba hacia abajo. Si ponemos más
printf, se ejecutan uno tras otro. El programa
change_780.c escribe la solución que encontramos a
mano para el vuelto:
// Escribe las monedas del vuelto de S/ 7.80, encontradas a mano.
// Resuelve un solo caso: el algoritmo general llega en las próximas semanas.
#include <stdio.h>
int main(void) {
printf("Vuelto de S/ 7.80:\n");
printf("S/ 5.00\n");
printf("S/ 2.00\n");
printf("S/ 0.50\n");
printf("S/ 0.20\n");
printf("S/ 0.10\n");
printf("Total: 5 monedas\n");
return 0;
}
$ gcc -Wall -Wextra -std=c17 change_780.c -o change_780
$ ./change_780
Vuelto de S/ 7.80:
S/ 5.00
S/ 2.00
S/ 0.50
S/ 0.20
S/ 0.10
Total: 5 monedas
Las líneas que empiezan con // son comentarios: notas para quien lee, que el
compilador ignora. La sangría de 4 espacios tampoco le importa al compilador, pero sin
ella el código es difícil de leer, así que en este curso es obligatoria.
Miren bien este programa, porque no es el algoritmo del vuelto. Solo recita una respuesta calculada a mano. Si el vuelto fuera S/ 3.40, habría que reescribirlo. Para que el programa calcule el vuelto por sí mismo le falta guardar valores y repetir. Eso llega en las semanas 2 y 5.
En C. Un programa en C tiene esta estructura:
#includearriba, luegomaincon sus instrucciones entre llaves, cada una terminada en;, y al finalreturn 0;. Por ahora, todos sus programas tendrán esta forma.
Cuando algo sale mal
Van a tener errores todo el semestre, igual que cualquier persona que programa. La diferencia entre sufrirlos y resolverlos es saber leerlos. Hay dos grandes tipos:
| Tipo | Cuándo aparece | ¿Hay ejecutable? |
|---|---|---|
| Error de compilación | Al correr gcc |
No |
| Error de ejecución | Al correr el programa | Sí, se compiló bien |
Errores de compilación
El programa rompe las reglas de C, y gcc se niega a traducirlo. Son los más fáciles,
porque el compilador dice dónde está el problema.
Le quitamos el ; a la línea de printf:
$ gcc -Wall -Wextra -std=c17 hola.c -o hola
hola.c: In function ‘main’:
hola.c:4:28: error: expected ‘;’ before ‘return’
4 | printf("Hola, mundo\n")
| ^
| ;
5 | return 0;
| ~~~~~~
La línea importante es la que tiene error:, y se lee por partes:
| Parte | Significa |
|---|---|
hola.c |
El archivo |
4 |
La línea |
28 |
La columna, contando caracteres desde la izquierda |
error |
Es un error: no hay ejecutable |
expected ‘;’ before ‘return’ |
“Se esperaba ; antes de return” |
Debajo, gcc copia la línea, marca el lugar con ^ y hasta sugiere qué poner. Noten
que menciona return, que está en la línea 5: encontró esa palabra cuando todavía
esperaba el ;. Por eso nombra la línea siguiente, aunque el ; falta al final de la
línea 4.
Ahora dejamos el ; y le quitamos la comilla que cierra el texto:
$ gcc -Wall -Wextra -std=c17 hola.c -o hola
hola.c: In function ‘main’:
hola.c:4:12: warning: missing terminating " character
4 | printf("Hola, mundo\n);
| ^
hola.c:4:12: error: missing terminating " character
4 | printf("Hola, mundo\n);
| ^~~~~~~~~~~~~~~~
hola.c:5:5: error: expected expression before ‘return’
5 | return 0;
| ^~~~~~
hola.c:5:14: error: expected ‘;’ before ‘}’ token
5 | return 0;
| ^
| ;
6 | }
| ~
¡Tres errores por una sola comilla! Es normal. Sin la comilla final, el ) y el ;
quedan como parte de un texto que nunca termina, y gcc pierde el hilo: los errores
de la línea 5 son consecuencia del primero. La línea 5 está bien. Si corrigen la línea
4, todos desaparecen.
Idea clave. Lean siempre el primer error, corríjanlo y vuelvan a compilar. Los que vienen después pueden ser consecuencia de ese. Y si la línea que señala
gccparece correcta, revisen también la anterior.
Si escriben mal el nombre de una función, gcc lo detecta y hasta propone un arreglo:
$ gcc -Wall -Wextra -std=c17 hola.c -o hola
hola.c: In function ‘main’:
hola.c:4:5: error: implicit declaration of function ‘print’; did you mean ‘printf’? [-Wimplicit-function-declaration]
4 | print("Hola, mundo\n");
| ^~~~~
| printf
Implicit declaration quiere decir que se está usando una función que nadie le
presentó. Lo mismo pasa si olvidan el #include, y gcc les dice qué línea falta:
$ gcc -Wall -Wextra -std=c17 hola.c -o hola
hola.c: In function ‘main’:
hola.c:2:5: error: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]
2 | printf("Hola, mundo\n");
| ^~~~~~
hola.c:1:1: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’
+++ |+#include <stdio.h>
1 | int main(void) {
hola.c:2:5: warning: incompatible implicit declaration of built-in function ‘printf’ [-Wbuiltin-declaration-mismatch]
2 | printf("Hola, mundo\n");
| ^~~~~~
hola.c:2:5: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’
Estos mensajes dependen de la versión de gcc. Los de estos apuntes salen de la
versión del entorno del curso, la 14.2. En versiones antiguas, algunos de estos errores
eran solo advertencias.
Un último caso. Si escriben Main con mayúscula, el mensaje se ve muy distinto:
$ gcc -Wall -Wextra -std=c17 hola.c -o hola
/usr/bin/ld: /usr/lib/gcc/aarch64-linux-gnu/14/../../../aarch64-linux-gnu/Scrt1.o: in function `_start':
(.text+0x1c): undefined reference to `main'
/usr/bin/ld: (.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
Para C, Main es una función cualquiera, así que el código es válido. El que se queja
es ld, el enlazador (paso 4): busca dónde empieza el programa y no encuentra ningún
main. Las rutas del mensaje dependen de la máquina. Lo que importa es *undefined
reference to main'*, "no hay ninguna definición de main`”.
Error común. Compilar con errores y luego ejecutar sin mirar. Cuando
gccencuentra un error en el código, como un;que falta, no toca el ejecutable anterior. Si ya existía unholade antes, sigue ahí, y./holaejecuta el programa viejo:$ gcc -Wall -Wextra -std=c17 hola.c -o hola hola.c: In function ‘main’: hola.c:4:28: error: expected ‘;’ before ‘return’ 4 | printf("Hola, mundo\n") | ^ | ; 5 | return 0; | ~~~~~~ $ ls hola hola.c $ ./hola Hola, mundoParece que el programa funciona, pero es el de la vez anterior. Lean siempre lo que dice
gccantes de ejecutar. Los errores del enlazador, como el deMain, son distintos: ahígccsí borra el ejecutable anterior.
Advertencias
Una advertencia (warning) no detiene la compilación: el ejecutable se crea. Pero
gcc les está diciendo que algo es sospechoso. En este curso el código no debe dar
advertencias. Tómenlas como errores que todavía no los han mordido.
Errores de ejecución
Aquí el programa compiló, pero falla o se porta mal al correr. Son más difíciles, porque nadie les señala una línea: puede que ni siquiera haya mensaje.
Resultado incorrecto. El programa errors/wrong_change.c
está bien escrito para gcc, que no dice nada. Corre y termina normalmente:
$ gcc -Wall -Wextra -std=c17 wrong_change.c -o wrong_change
$ ./wrong_change
Vuelto de S/ 7.80:
S/ 5.00
S/ 2.00
S/ 1.00
S/ 0.50
S/ 0.20
S/ 0.10
Total: 6 monedas
Esas monedas suman S/ 8.80. El cliente recibe un sol de más, y solo se nota si alguien compara el resultado con lo que debía salir. Por eso existen las pruebas, y por eso siguen sus algoritmos a mano.
Salida con otro aspecto. Este es un error pequeño y muy común. Sin el \n, el
saludo queda pegado al prompt. Lo siguiente (pwd) es lo que se escribió después:
$ ./hola
Hola, mundo$ pwd
/home/ana/lab01
El programa hace algo inválido. El programa
errors/divide_by_zero.c intenta dividir
entre cero. El %d de printf significa “aquí va un número entero”. Lo verán bien la
próxima semana; por ahora, cópienlo tal cual.
// Dividir entre cero: gcc avisa, pero igual crea el ejecutable.
#include <stdio.h>
int main(void) {
printf("Antes de dividir\n");
printf("%d\n", 10 / 0);
printf("Después de dividir\n");
return 0;
}
Primero, sin ninguna ayuda adicional:
$ gcc -Wall -Wextra -std=c17 divide_by_zero.c -o divide_by_zero
divide_by_zero.c: In function ‘main’:
divide_by_zero.c:6:23: warning: division by zero [-Wdiv-by-zero]
6 | printf("%d\n", 10 / 0);
| ^
$ ./divide_by_zero
Antes de dividir
0
Después de dividir
gcc avisó y creó el ejecutable. En la máquina donde preparamos estos apuntes, escribe
un 0 y sigue, como si nada. Ese 0 no significa nada: C no define qué pasa al dividir
entre cero, así que cualquier resultado es posible. En un procesador de otro tipo, el
programa puede terminar de golpe en esa línea, sin llegar a la última. Un programa con
este error no es confiable, aunque hoy parezca funcionar.
Por eso en los laboratorios se compila además con -g -fsanitize=address,undefined.
-fsanitize agrega controles que vigilan el programa mientras corre, y -g guarda datos
para que los mensajes puedan indicar el archivo y la línea:
$ gcc -Wall -Wextra -std=c17 -g -fsanitize=address,undefined divide_by_zero.c -o divide_by_zero
divide_by_zero.c: In function ‘main’:
divide_by_zero.c:6:23: warning: division by zero [-Wdiv-by-zero]
6 | printf("%d\n", 10 / 0);
| ^
$ ./divide_by_zero
Antes de dividir
divide_by_zero.c:6:23: runtime error: division by zero
0
Después de dividir
Runtime error es “error de ejecución”, y trae archivo, línea y columna. Cuando vean cómo se dañan cosas en la memoria, en las semanas 9 y 10, estas opciones les van a ahorrar horas.
El laboratorio, paso a paso
Los enunciados están en la guía de la semana. Aquí van las ideas que necesitan para cada punto.
Punto 1: abrir el entorno
Todo el curso se trabaja en un mismo entorno: una máquina Linux con gcc, Git y
todo lo demás ya instalado. Lo normal es abrirlo en el navegador con GitHub Codespaces;
quien tenga laptop también puede usarlo con Docker. Los pasos están en
labs/README.md.
Una vez abierto, comprueben que todo funciona, desde la carpeta principal del curso:
$ env/check.sh
ok gcc compila C17 sin advertencias
ok el programa en C se ejecuta
ok el sanitizer compila
ok el sanitizer detecta un error de memoria
ok make
ok gdb
ok git
ok Python 3.14
Si alguna línea dice FALLA, avisen al docente.
Punto 2: moverse por las carpetas
Su trabajo de esta semana va en code/week-01/lab. Desde la carpeta principal:
$ cd code/week-01/lab
$ ls
change.txt compiler_errors.txt
$ mkdir lab01
$ cd lab01
$ pwd
pwd les mostrará una ruta que termina en code/week-01/lab/lab01. El inicio depende
de dónde abrieron el entorno.
Punto 3: el primer programa
Escriban hola.c dentro de lab01, con cualquiera de los editores. Escríbanlo a
mano, sin copiar y pegar: los dedos también aprenden. Luego compilen y ejecuten con los
comandos que vieron arriba. Debe escribir exactamente Hola, mundo.
Punto 4: romperlo a propósito
Borren el ; de printf, compilen y lean. Vuelvan a ponerlo. Luego borren la comilla
que cierra el texto, compilen y lean. Anoten en lab/compiler_errors.txt en qué línea avisa
gcc y qué dice. Abran ese archivo con el editor y cambien cada ___ por su respuesta.
Al final, dejen hola.c correcto.
Punto 5: el vuelto a mano
En lab/change.txt está el inicio del algoritmo. Ahí mismo están los símbolos ← y ≥
para copiarlos. Complétenlo, sin mirar estos apuntes si pueden. Luego llenen la tabla para vuelto = 780, con una fila por moneda.
Comprobar con make
Escribir el comando de gcc cada vez se vuelve pesado, y más cuando un proyecto tiene
muchos archivos. make es una herramienta que lee un archivo llamado Makefile, donde
están escritas las recetas, y ejecuta los comandos necesarios. Cada carpeta de código
del curso tiene el suyo. Desde code/week-01:
| Comando | Qué hace |
|---|---|
make |
Compila los programas de la clase y deja los ejecutables en build/ |
make test |
Corre las pruebas de la semana |
make test-lab |
Revisa su laboratorio |
make clean |
Borra lo generado |
Por ejemplo:
$ make
mkdir -p build
gcc -Wall -Wextra -std=c17 -g -fsanitize=address,undefined hola.c -o build/hola
gcc -Wall -Wextra -std=c17 -g -fsanitize=address,undefined change_780.c -o build/change_780
gcc -Wall -Wextra -std=c17 -g -fsanitize=address,undefined exit_code.c -o build/exit_code
$ build/hola
Hola, mundo
Una prueba (test) es un programa que comprueba que otro hace lo que debe. Las de
make test-lab compilan su hola.c sin permitir advertencias, revisan lo que escribe
y miran sus dos archivos de texto. Están hechas para fallar hasta que terminen. Antes de
empezar, este es el inicio de lo que se ve:
$ make test-lab
LAB_DIR=lab python3 -m unittest test_lab
FFFF
======================================================================
FAIL: test_folder_exists (test_lab.HolaTest.test_folder_exists)
----------------------------------------------------------------------
AssertionError: No existe la carpeta lab/lab01. Créenla con mkdir.
Cada F es una prueba que falló, y debajo está el motivo. Cuando todo esté bien,
verán esto, con otro tiempo porque depende de la máquina:
$ make test-lab
LAB_DIR=lab python3 -m unittest test_lab
....
----------------------------------------------------------------------
Ran 4 tests in 0.023s
OK
El código de la semana
Todo está en code/week-01: los programas del capítulo, la
carpeta errors/ con los programas que tienen errores a propósito, la carpeta lab/
con los archivos del laboratorio, y las pruebas, escritas en Python, en los archivos
test_examples.py, test_algorithms.py y test_lab.py.
No hace falta que entiendan las pruebas por ahora. Pero noten qué hacen:
comprueban lo que dicen estos apuntes. Los mensajes de gcc de este capítulo, por
ejemplo, se comparan con lo que gcc escribe de verdad. make test debe terminar así,
con un tiempo que depende de la máquina:
$ make test
mkdir -p build
gcc -Wall -Wextra -std=c17 -g -fsanitize=address,undefined hola.c -o build/hola
gcc -Wall -Wextra -std=c17 -g -fsanitize=address,undefined change_780.c -o build/change_780
gcc -Wall -Wextra -std=c17 -g -fsanitize=address,undefined exit_code.c -o build/exit_code
python3 -m unittest test_examples test_algorithms
............................
----------------------------------------------------------------------
Ran 28 tests in 1.270s
OK
Resumen
- Computar es seguir pasos sin agregar criterio propio.
- Un algoritmo es una secuencia finita de pasos precisos y sin ambigüedad, que recibe datos y produce un resultado.
- El pseudocódigo describe un algoritmo para personas.
←asigna,mientrasrepite ysidecide. Un diagrama de flujo dibuja lo mismo. - Seguir un algoritmo a mano con una tabla de variables es la forma de entenderlo y de encontrarle errores. Un ejemplo que sale bien no prueba que sea correcto.
- Una terminal es la ventana; el shell ejecuta los comandos; el prompt
indica que está listo.
pwd,ls,cd,mkdirycatbastan para moverse. Una ruta absoluta empieza con/, una relativa parte de la carpeta actual, y..es la carpeta de arriba. - Un compilador traduce el código fuente a un ejecutable. La CPU solo ejecuta código máquina, y este depende del procesador.
- El ciclo de trabajo en C es escribir, compilar y ejecutar:
gcc -Wall -Wextra -std=c17 hola.c -o holay luego./hola. - Todo programa en C empieza en
main, y sus instrucciones se ejecutan en orden y terminan en;.return 0significa que todo salió bien. - Un error de compilación impide crear el ejecutable. Lean el primero, corríjanlo y compilen de nuevo. Un error de ejecución aparece al correr el programa, y a veces sin ningún mensaje.
Ejercicios
-
¿Cuáles de estas instrucciones son un algoritmo? Para las que no lo son, expliquen por qué, usando las condiciones de esta semana. a) “Ordena estos números.” b) “Sal de casa temprano.” c) “Empieza en 1. Escribe el número y súmale 2. Repite para siempre.” d) “Escribe 1. Escribe 2. Escribe 3. Termina.”
-
Reescriban como pasos precisos las instrucciones para prender una cocina a gas y hervir agua. Pásenselas a otra persona: que busque un paso en el que deba adivinar.
-
Sigan
dar_vueltoa mano convuelto = 1360, con una tabla como la del capítulo. ¿Qué monedas salen y cuántas son? -
Sigan
dar_vueltoconvuelto = 785. ¿Qué valor tienevueltoal terminar? Agreguen al algoritmo unsial final que escribafalta entregarsi quedó algo sin entregar. -
Escriban en pseudocódigo un algoritmo que reciba tres números y escriba el mayor. Síganlo a mano con
4, 9, 2. Luego pruébenlo con9, 9, 2y con2, 2, 2. -
Dibujen el diagrama de flujo de
mayor_de_dos. ¿Tiene alguna flecha hacia atrás? ¿Por qué? -
Un país tiene monedas de 1, 5 y 7. Sigan el método de “la más grande que cabe” para un vuelto de 10. Luego encuentren una forma de darlo con menos monedas. ¿Qué concluyen sobre este método?
-
Estando en
/home/ana/lab01, ¿a qué carpeta llevancd ..ycd ../..? ¿Ycd /? ¿Ycd ~, si la carpeta personal es/home/ana? Respondan primero y luego comprueben con carpetas propias usandomkdir,cdypwd. -
¿Qué archivo ejecuta la computadora:
hola.cohola? ¿Por qué? ¿Qué pasa conholasi cambianhola.cy no compilan de nuevo? Compruébenlo. -
Sin compilar, predigan para cada cambio: ¿compila?, si compila, ¿qué escribe?, ¿qué responde
echo $?después de ejecutarlo? Luego compruébenlo. Todos parten dehola.c. a) CambianprintfporPrintf. b) Cambian"Hola, mundo\n"por"Hola, mundo". c) Borran la llave}final. d) Escriben todo el cuerpo demainen una sola línea. e) Cambianreturn 0;porreturn 3;. -
Escriban un programa
receipt.cque escriba una boleta de una bodega con tres productos y sus precios, un producto por línea, y el total. Los montos los calculan ustedes a mano. Compílenlo sin advertencias. ¿Qué tendrían que cambiar si el cliente compra otro producto? ¿Por qué esto todavía no es un algoritmo para calcular el total? -
Investiguen con la terminal, sin buscar afuera: creen la carpeta
prueba, entren, y compilen suhola.csin la opción-o, indicando su ruta (por ejemplo,gcc ../hola.c). ¿Cómo se llama el ejecutable? ¿Cómo lo ejecutan? ¿Qué diceecho $?justo después de ejecutarlo y justo después de un comando que falla, comocd no_existe?