Funciones en C — ejercicios resueltos con la guagua: segundo peor retraso, paradas que se cierran y abren y horarios que se fusionan (más el chuletario para el examen)
En el simulacro de IC2 que hicimos en clase, mi función para quitar una parada de la línea funcionaba perfecta… dentro de la función. En main la línea seguía teniendo cinco paradas, porque el tamaño n lo había pasado por valor. Dos caracteres, un * en el parámetro y un & en la llamada, separaban el programa bueno del malo. Estos ejercicios están pensados para que esos dos caracteres te salgan solos.
Son los ejercicios de la teoría y la práctica de funciones en C, con la línea 201 de la guagua. Cómo sacarles partido: lee el enunciado y la salida esperada, escribe las funciones con sus prototipos arriba, compila con -Wall (o pruébalo en el entorno de C de la web), abre la pista solo si llevas un rato atascado y compara con la solución comentada. Antes de las soluciones tienes tres fragmentos de «¿Qué falla aquí?» y al final, el chuletario completo de funciones. Todas las salidas son de una ejecución real con gcc 14.
Tabla de Contenidos
Nivel básico
Ejercicio 1: el segundo peor retraso
Queremos saber el peor retraso del día y también el segundo peor, que tiene que ser un valor distinto del máximo (si el 85 aparece dos veces, el segundo no es 85). Escribe un programa con estas funciones:
int maximo(int v[], int n): devuelve el valor máximo.int segundo_maximo(int v[], int n): devuelve el mayor valor estrictamente menor que el máximo, o -1 si no hay (todos iguales).void mostrar(int v[], int n)yvoid informe(int v[], int n), que imprime el vector, el máximo y el segundo.
Pruébalo con los retrasos del lunes {42, 7, 19, 85, 26, 85, 54, 13}, del martes {10, 10, 10, 10} y del miércoles {9, 31}.
Salida esperada:
Retrasos: 42 7 19 85 26 85 54 13 Máximo: 85 Segundo máximo: 54 Retrasos: 10 10 10 10 Máximo: 10 Segundo máximo: no hay (todos iguales) Retrasos: 9 31 Máximo: 31 Segundo máximo: 9
No hace falta ordenar. Primero calcula el máximo llamando a maximo (reutiliza la función). Después recorre el vector buscando el mayor valor que cumpla v[i] < max. Empieza ese segundo máximo en -1: como los retrasos nunca son negativos, si al final sigue en -1 es que no había ninguno distinto.
Nivel intermedio
Ejercicio 2: cerrar y abrir paradas
La línea 201 guarda los códigos de sus paradas en un vector de hasta 10 casillas, de las que se usan n. Por obras cierran una parada y abren otra. Escribe:
int eliminar_posicion(int v[], int *n, int pos): quita la parada de la posiciónpos, corre las demás a la izquierda, actualiza*ny devuelve el código quitado. Si la posición no es válida, devuelve -1 sin tocar nada.int insertar_posicion(int v[], int *n, int pos, int valor, int max): metevalorenpos, corre las demás a la derecha y actualiza*n. Devuelve 1 si ha podido y 0 si la posición no es válida o no queda sitio.void mostrar(int v[], int n).
Con {101, 104, 112, 118, 125}: cierra la posición 2, abre la 120 en la posición 3, prueba a quitar la posición 9 e intenta meter la 99 con un máximo de 5.
Salida esperada:
Línea (5 paradas): 101 104 112 118 125 Eliminada la 112 de la posición 2 Línea (4 paradas): 101 104 118 125 Insertada la 120 en la posición 3 Línea (5 paradas): 101 104 118 120 125 Posición 9 no válida: no cambio nada Sin sitio para la 99 Línea (5 paradas): 101 104 118 120 125
El tamaño cambia, así que n va por referencia: int *n en el parámetro y &n en la llamada; dentro, *n para leerlo y (*n)-- o (*n)++ para cambiarlo. Para quitar, recorre de la posición hacia la derecha copiando v[i + 1] en v[i]. Para insertar, de *n hacia la posición copiando v[i - 1] en v[i]. Insertar en pos == *n es válido: es añadir al final.
Desafío final
Ejercicio 3: fusionar dos horarios ordenados
Las líneas 201 y 202 comparten parada en San Telmo. Cada una tiene su horario de salidas ordenado (números hhmm) y queremos el horario conjunto, también ordenado. Escribe:
void fusionar(int a[], int na, int b[], int nb, int res[], int *nr): mezclaayb, los dos ordenados, enres, que queda ordenado; deja en*nrel número de salidas.void ordenar(int v[], int n): ordena un vector (el de la burbuja vale).void mostrar(const char *titulo, int v[], int n).
Fusiona la 201 {610, 710, 810, 910, 1010} con la 202 {640, 700, 820, 950, 1100, 1210, 1300}. Después ordena la 303, que llega desordenada {1500, 730, 1215, 905}, y fusiónala con la 201. Por último, fusiona la 201 con un horario vacío.
Salida esperada:
Línea 201: 610 710 810 910 1010 Línea 202: 640 700 820 950 1100 1210 1300 Juntas: 610 640 700 710 810 820 910 950 1010 1100 1210 1300 Salidas: 12 Línea 303 ordenada: 730 905 1215 1500 201 + 303: 610 710 730 810 905 910 1010 1215 1500 201 + nada: 610 710 810 910 1010
No juntes los dos vectores para ordenar después: aprovecha que ya están ordenados. Usa tres índices, i para a, j para b y k para res. Mientras queden salidas en los dos, copia la menor de las dos «cabezas» (a[i] o b[j]) y avanza su índice. Cuando uno se acabe, copia lo que quede del otro. Prueba siempre el caso de un vector vacío (nb = 0).
¿Qué falla aquí?
Tres fragmentos con un error típico de funciones cada uno. Lee el código, predice qué imprimirá y después mira la explicación.
Fragmento 1: el saldo que no bajaba
#include <stdio.h>
void cobrar(int saldo, int precio) {
saldo = saldo - precio;
}
int main(void) {
int saldo = 1000;
cobrar(saldo, 155);
cobrar(saldo, 155);
printf("Saldo tras dos viajes: %d\n", saldo);
return 0;
}
Dos viajes de 1,55 euros sobre un bono de 10. ¿Qué saldo imprime?
Saldo tras dos viajes: 1000
Sigue en 1000. cobrar recibe una copia del saldo, le resta el viaje y la copia muere al salir de la función. gcc no avisa: el programa es correcto en C, solo que no hace lo que querías. Arreglo: la función recibe la dirección y escribe a través de ella.
#include <stdio.h>
void cobrar(int *saldo, int precio) {
*saldo = *saldo - precio; /* escribe en la variable de main */
}
int main(void) {
int saldo = 1000;
cobrar(&saldo, 155);
cobrar(&saldo, 155);
printf("Saldo tras dos viajes: %d\n", saldo);
return 0;
}
Saldo tras dos viajes: 690
1000 − 155 − 155 = 690, dos viajes cobrados de verdad.
Fragmento 2: contar las casillas dentro de la función
#include <stdio.h>
int paradas_llenas(int v[]) {
int n = sizeof(v) / sizeof(v[0]); /* ¿cuántas casillas tiene? */
int c = 0;
for (int i = 0; i < n; i++)
if (v[i] > 40) c++;
return c;
}
int main(void) {
int viajeros[6] = {15, 48, 42, 3, 51, 19};
printf("Paradas con más de 40: %d\n", paradas_llenas(viajeros));
return 0;
}
Hay tres paradas con más de 40 viajeros (48, 42 y 51). ¿Cuántas cuenta la función?
contar.c: In function 'paradas_llenas': contar.c:4: warning: 'sizeof' on array function parameter 'v' will return size of 'int *' [-Wsizeof-array-argument] contar.c:3: note: declared here
Paradas con más de 40: 1
Solo una. Dentro de la función, v es un puntero, así que sizeof(v) da 8 y 8 / 4 son 2 casillas: la función solo mira 15 y 48. gcc lo avisa con -Wsizeof-array-argument. Arreglo: el tamaño se calcula en main, donde viajeros sí es el vector, y se pasa como parámetro.
#include <stdio.h>
int paradas_llenas(int v[], int n) { /* el tamaño viaja aparte */
int c = 0;
for (int i = 0; i < n; i++)
if (v[i] > 40) c++;
return c;
}
int main(void) {
int viajeros[6] = {15, 48, 42, 3, 51, 19};
int n = sizeof viajeros / sizeof viajeros[0]; /* aquí sí: en main es el vector */
printf("Paradas con más de 40: %d\n", paradas_llenas(viajeros, n));
return 0;
}
Paradas con más de 40: 3
Fragmento 3: el billete de 3 zonas
#include <stdio.h>
int precio(int zonas) {
if (zonas == 1)
return 155;
else if (zonas == 2)
return 175;
}
int main(void) {
for (int z = 1; z <= 3; z++)
printf("%d zonas: %d céntimos\n", z, precio(z));
return 0;
}
¿Cuánto cuesta el billete de 3 zonas?
precio.c: In function 'precio': precio.c:8: warning: control reaches end of non-void function [-Wreturn-type]
1 zonas: 155 céntimos 2 zonas: 175 céntimos 3 zonas: 3 céntimos
3 céntimos. Con 3 zonas ninguno de los dos if se cumple, la función llega a su llave de cierre sin return y devuelve lo que hubiera en el registro de retorno; en esta ejecución, un 3 (probablemente el valor de zonas, que se había quedado en ese registro). En otro ordenador saldrá otra cosa. gcc lo avisa con control reaches end of non-void function: ese aviso nunca se ignora. Arreglo: que todos los caminos acaben en un return.
#include <stdio.h>
int precio(int zonas) {
if (zonas == 1)
return 155;
else if (zonas == 2)
return 175;
return 195; /* 3 zonas o más: todos los caminos devuelven */
}
int main(void) {
for (int z = 1; z <= 3; z++)
printf("%d zonas: %d céntimos\n", z, precio(z));
return 0;
}
1 zonas: 155 céntimos 2 zonas: 175 céntimos 3 zonas: 195 céntimos
Soluciones comentadas
Solución del ejercicio 1
#include <stdio.h>
int maximo(int v[], int n);
int segundo_maximo(int v[], int n);
void mostrar(int v[], int n);
void informe(int v[], int n);
int main(void) {
int lunes[8] = {42, 7, 19, 85, 26, 85, 54, 13}; /* retrasos en minutos */
int martes[4] = {10, 10, 10, 10};
int miercoles[2] = {9, 31};
informe(lunes, 8);
informe(martes, 4);
informe(miercoles, 2);
return 0;
}
int maximo(int v[], int n) {
int m = v[0];
for (int i = 1; i < n; i++)
if (v[i] > m) m = v[i];
return m;
}
/* El mayor valor ESTRICTAMENTE menor que el máximo, o -1 si no hay */
int segundo_maximo(int v[], int n) {
int max = maximo(v, n); /* primera pasada, reutilizando */
int segundo = -1; /* -1: todavía no he visto ninguno */
for (int i = 0; i < n; i++)
if (v[i] < max && v[i] > segundo) /* distinto del máximo y mejor que el actual */
segundo = v[i];
return segundo;
}
void mostrar(int v[], int n) {
for (int i = 0; i < n; i++)
printf("%d ", v[i]);
printf("\n");
}
void informe(int v[], int n) {
printf("Retrasos: ");
mostrar(v, n);
printf("Máximo: %d\n", maximo(v, n));
int s = segundo_maximo(v, n);
if (s == -1)
printf("Segundo máximo: no hay (todos iguales)\n\n");
else
printf("Segundo máximo: %d\n\n", s);
}
Las decisiones que importan:
segundo_maximollama amaximoen lugar de repetir el bucle: dos pasadas sencillas valen más que una enrevesada.- La condición es doble:
v[i] < maxdescarta el máximo (aunque esté repetido) yv[i] > segundose queda con el mejor de los que quedan. - -1 como «no hay»: funciona porque los retrasos son minutos no negativos. Si los datos pudieran ser negativos, habría que devolver el resultado por puntero y un 1/0 con
return, comobuscar_posen la práctica. informeevita repetir las cuatro líneas de impresión tres veces: cada día es una llamada.
Traza del lunes ({42, 7, 19, 85, 26, 85, 54, 13}, máximo 85):
v[i] | ¿< 85? | ¿> segundo? | segundo |
|---|---|---|---|
| 42 | Sí | Sí (42 > -1) | 42 |
| 7, 19 | Sí | No | 42 |
| 85 | No | — | 42 |
| 26 | Sí | No | 42 |
| 85 | No | — | 42 |
| 54 | Sí | Sí | 54 |
| 13 | Sí | No | 54 |
Solución del ejercicio 2
#include <stdio.h>
#define MAX 10
int eliminar_posicion(int v[], int *n, int pos);
int insertar_posicion(int v[], int *n, int pos, int valor, int max);
void mostrar(int v[], int n);
int main(void) {
int paradas[MAX] = {101, 104, 112, 118, 125}; /* códigos de la línea */
int n = 5;
mostrar(paradas, n);
int quitada = eliminar_posicion(paradas, &n, 2); /* cierran la parada 112 */
printf("Eliminada la %d de la posición 2\n", quitada);
mostrar(paradas, n);
if (insertar_posicion(paradas, &n, 3, 120, MAX)) /* abren la 120 */
printf("Insertada la 120 en la posición 3\n");
mostrar(paradas, n);
if (eliminar_posicion(paradas, &n, 9) == -1) /* posición que no existe */
printf("Posición 9 no válida: no cambio nada\n");
if (!insertar_posicion(paradas, &n, 0, 99, 5)) /* ya hay 5 y el máximo es 5 */
printf("Sin sitio para la 99\n");
mostrar(paradas, n);
return 0;
}
/* Quita v[pos], corre el resto a la izquierda y actualiza *n */
int eliminar_posicion(int v[], int *n, int pos) {
if (pos < 0 || pos >= *n)
return -1; /* no válida: no toco nada */
int quitado = v[pos];
for (int i = pos; i < *n - 1; i++) /* de izquierda a derecha */
v[i] = v[i + 1];
(*n)--;
return quitado;
}
/* Mete valor en pos, corre el resto a la derecha; 1 si pudo, 0 si no */
int insertar_posicion(int v[], int *n, int pos, int valor, int max) {
if (pos < 0 || pos > *n || *n >= max) /* pos == *n vale: añadir al final */
return 0;
for (int i = *n; i > pos; i--) /* de derecha a izquierda */
v[i] = v[i - 1];
v[pos] = valor;
(*n)++;
return 1;
}
void mostrar(int v[], int n) {
printf("Línea (%d paradas): ", n);
for (int i = 0; i < n; i++)
printf("%d ", v[i]);
printf("\n");
}
int *nen las dos funciones: el tamaño de la línea vive enmainy las funciones lo cambian. Dentro se escribe*n(y(*n)--, con paréntesis); en la llamada,&n.- La validación va primero:
pos >= *nen eliminar (no hay casilla*n) ypos > *nen insertar (sí se puede insertar justo al final), más*n >= maxpara no escribir fuera del vector. - Las direcciones de recorrido son opuestas: al quitar la 112 se copia de izquierda a derecha (118 a la casilla 2, 125 a la 3); al meter la 120 se copia de derecha a izquierda (125 de la casilla 3 a la 4) y luego se escribe la 120.
- Los valores de retorno informan: el código quitado (o -1) y 1/0 al insertar.
maindecide qué mensaje dar.
Solución del ejercicio 3
#include <stdio.h>
#define MAX 20
void fusionar(int a[], int na, int b[], int nb, int res[], int *nr);
void ordenar(int v[], int n);
void mostrar(const char *titulo, int v[], int n);
int main(void) {
int l201[5] = {610, 710, 810, 910, 1010}; /* ya ordenadas (hhmm) */
int l202[7] = {640, 700, 820, 950, 1100, 1210, 1300};
int todas[MAX], n;
mostrar("Línea 201: ", l201, 5);
mostrar("Línea 202: ", l202, 7);
fusionar(l201, 5, l202, 7, todas, &n);
mostrar("Juntas: ", todas, n);
printf("Salidas: %d\n\n", n);
int l303[4] = {1500, 730, 1215, 905}; /* desordenadas */
ordenar(l303, 4);
mostrar("Línea 303 ordenada: ", l303, 4);
fusionar(l201, 5, l303, 4, todas, &n);
mostrar("201 + 303: ", todas, n);
int vacia[1];
fusionar(l201, 5, vacia, 0, todas, &n); /* nb = 0 */
mostrar("201 + nada: ", todas, n);
return 0;
}
/* Mezcla dos vectores ordenados en res, también ordenado */
void fusionar(int a[], int na, int b[], int nb, int res[], int *nr) {
int i = 0, j = 0, k = 0;
while (i < na && j < nb) { /* mientras queden en los dos */
if (a[i] <= b[j])
res[k++] = a[i++]; /* el menor de las dos cabezas */
else
res[k++] = b[j++];
}
while (i < na) res[k++] = a[i++]; /* lo que sobre de a */
while (j < nb) res[k++] = b[j++]; /* o lo que sobre de b */
*nr = k; /* el tamaño sale por puntero */
}
void ordenar(int v[], int n) {
for (int i = 0; i < n - 1; i++)
for (int j = 0; j < n - 1 - i; j++)
if (v[j] > v[j + 1]) {
int t = v[j]; v[j] = v[j + 1]; v[j + 1] = t;
}
}
void mostrar(const char *titulo, int v[], int n) {
printf("%s", titulo);
for (int i = 0; i < n; i++)
printf("%d ", v[i]);
printf("\n");
}
- Tres índices que solo avanzan: cada salida se mira una vez, así que fusionar 5 + 7 salidas cuesta 12 pasos. Ordenar al final costaría muchos más.
<=en la comparación: si las dos líneas salieran a la misma hora, primero se copia la dea. Con<también funcionaría;<=mantiene el orden de origen, que en otros problemas importa.- Los dos
whilefinales: solo uno de ellos llega a ejecutarse, el del vector que no se ha acabado. Con la 202, las tres últimas salidas (1100, 1210 y 1300) entran ahí. *nr = kal final:kha contado cada salida copiada; el tamaño llega amainpor el puntero. Con el horario vacío (nb = 0) el primerwhileni empieza y se copia la 201 entera.
Para seguir practicando
Si los tres ejercicios te han salido, estas variantes repasan lo mismo desde otro ángulo. Están pensadas para el tipo de pregunta que cae en el práctico, donde te piden una función concreta y la llamada desde main:
- Segundo máximo en una sola pasada. Reescribe
segundo_maximosin llamar amaximo, manteniendo dos variables (primeroysegundo) que se actualizan en el mismo bucle. Cuidado con el caso en que aparece un valor nuevo mayor queprimero: el viejoprimeropasa a ser elsegundo. - Eliminar por valor. Escribe
int eliminar_valor(int v[], int *n, int codigo), que busque la parada por su código (reutilizando una búsqueda) y llame aeliminar_posicion. Devuelve 1 si la quitó y 0 si no existía. - Eliminar todas las apariciones. Con la lista de líneas repetidas de la práctica, quita todas las veces que aparece la 201 y comprueba que
*nacaba en el número correcto. - Fusionar sin repetidos. Modifica
fusionarpara que, si las dos líneas salen a la misma hora, esa hora aparezca una sola vez en el horario conjunto. Pista: cuandoa[i] == b[j], copia uno y avanza los dos índices.
En todas ellas, antes de compilar, escribe primero los prototipos y marca con una flecha qué parámetros son de salida: si una función cambia algo de main, ese parámetro lleva * y la llamada lleva &.
Míralo paso a paso en Python Tutor
En Python Tutor, eligiendo C, puedes ver cómo eliminar_posicion cambia a la vez el vector y el tamaño de main. Pega esta versión corta:
#include <stdio.h>
int eliminar_posicion(int v[], int *n, int pos) {
if (pos < 0 || pos >= *n) {
return -1;
}
int quitado = v[pos];
for (int i = pos; i < *n - 1; i++) {
v[i] = v[i + 1];
}
(*n)--;
return quitado;
}
int main(void) {
int paradas[5] = {101, 104, 112, 118, 125};
int n = 5;
int q = eliminar_posicion(paradas, &n, 2);
printf("quitada %d, quedan %d\n", q, n);
return 0;
}
Qué observar paso a paso:
- Al entrar en la función,
vapunta al vectorparadasynapunta a la variablendemain: dos flechas. quitadoguarda el 112 antes de que nadie lo pise.- En el
for, las casillas 2 y 3 deparadasreciben 118 y 125: el cambio se ve directamente en el marco demain. - En
(*n)--la cajandemainpasa de 5 a 4. Si hubieras escrito*n--, verías moverse la flecha y el 5 quedaría intacto. - Al volver,
qrecibe 112 y el programa imprimequitada 112, quedan 4.
Chuletario: funciones en C
Lo esencial en 30 segundos
Una función tiene tipo de retorno, nombre, parámetros con tipo y cuerpo. El prototipo (la firma con ;) va arriba para poder llamarla antes de definirla. void significa «no devuelve nada» o «no recibe nada». C pasa los argumentos por valor (copias); para modificar una variable de quien llama, se pasa su dirección. Un vector se pasa como dirección y su tamaño, aparte.
Declarar, definir y llamar
| Quiero… | Código | Resultado |
|---|---|---|
| Un prototipo | int precio_billete(int zonas); | se puede llamar antes de definirla |
| Una función que devuelve | int f(int x) { return x * 2; } | f(3) vale 6 |
| Una función sin retorno | void separador(int largo) | hace algo y no devuelve nada |
| Sin parámetros | int primera_salida(void) | se llama primera_salida() |
Salir antes de una void | return; | termina sin valor |
| El main correcto | int main(void) { …; return 0; } | código de salida 0 |
Valor, referencia y vectores
| Quiero… | Código | Resultado |
|---|---|---|
| Que solo lea un dato | void f(int x) y f(saldo) | trabaja con una copia |
| Que modifique un dato | void f(int *x) y f(&saldo) | dentro, *x = … |
| Incrementar lo apuntado | (*n)++; | con paréntesis |
| Devolver dos resultados | int f(…, int *pos) | uno por return, otro por *pos |
| Pasar un vector | void f(int v[], int n) y f(v, n) | puede modificarlo |
| Un vector de solo lectura | void f(const int v[], int n) | gcc impide escribir |
| Tamaño de un vector | sizeof v / sizeof v[0] en main | nunca dentro de la función |
| Cambiar un elemento por puntero | intercambiar(&v[j], &v[j + 1]) | & de cada casilla |
Plantillas de los ejercicios
/* Segundo máximo distinto del máximo (ejercicio 1) */
int max = maximo(v, n), segundo = -1;
for (int i = 0; i < n; i++)
if (v[i] < max && v[i] > segundo) segundo = v[i];
/* Eliminar en una posición con el tamaño por referencia (ejercicio 2) */
if (pos < 0 || pos >= *n) return -1;
for (int i = pos; i < *n - 1; i++) v[i] = v[i + 1];
(*n)--;
/* Insertar en una posición (ejercicio 2) */
for (int i = *n; i > pos; i--) v[i] = v[i - 1];
v[pos] = valor;
(*n)++;
/* Fusionar dos vectores ordenados (ejercicio 3) */
while (i < na && j < nb)
res[k++] = (a[i] <= b[j]) ? a[i++] : b[j++];
while (i < na) res[k++] = a[i++];
while (j < nb) res[k++] = b[j++];
*nr = k;
Resultados con los datos de los ejercicios: segundo peor retraso del lunes 54 (máximo 85); la línea queda 101 104 118 120 125; el horario conjunto de la 201 y la 202 tiene 12 salidas, de 610 a 1300.
Síntoma y causa probable
| Síntoma | Causa probable |
|---|---|
implicit declaration of function | falta el prototipo o el #include |
conflicting types for | el prototipo y la definición no coinciden |
La variable de main no cambia | se pasó por valor: falta * en el parámetro y & en la llamada |
| Un tamaño que no se actualiza | n pasado por valor, o *n-- en vez de (*n)-- |
sizeof on array function parameter | sizeof de un vector dentro de la función |
control reaches end of non-void function | un camino sin return |
| Un número absurdo como resultado | la función devolvió basura por un camino sin return |
return type of 'main' is not 'int' | void main en lugar de int main(void) |
Antes de entregar, pregúntate
- ¿Están todos los prototipos arriba y coinciden con sus definiciones?
- ¿Las variables que una función tiene que modificar llegan como punteros, con
&en la llamada? - ¿Cada función que recibe un vector recibe también su tamaño?
- ¿Todos los caminos de las funciones que devuelven algo terminan en un
return?
Con las funciones dominadas, el siguiente tema es Memoria dinámica en C: malloc devuelve una dirección, y las funciones que acabas de escribir te servirán para trabajar con vectores que crecen mientras el programa corre.
¿Te ha servido este artículo? Así puedes apoyar SergioLearns →




Qué es un sistema operativo — teoría con tu portátil en una tarde de estudio, una llamada a write() y la CPU que nunca descansa (y los tres programas que no se ejecutaban a la vez)