Prerrequisitos y Entorno de Ejecución Requerido¶
Para compilar y verificar las soluciones de este módulo bajo el estándar C11 estricto de cátedra, se requiere:
Compilador C11: GCC 9+ o Clang 11+ configurado con flags
-Wall -Wextra -Werror -pedantic -std=c11.Entorno POSIX: Linux o WSL con utilidades estándar, soporte de flujos
stdin/stdouty aserciones.Herramientas de Verificación: Valgrind (memcheck) y AddressSanitizer (
-fsanitize=address,undefined) para garantizar la ausencia de desbordamientos de búfer en llamadas asscanf/sprintf.Conocimientos Previos: Función principal
main, tipos primitivos (int,float,char), especificadores de formato deprintfy validación defensiva del valor de retorno descanf.
Objetivos Pedagógicos y Competencias (Taxonomía de Bloom)¶
Nivel 2 (Comprensión): Comprender el mecanismo de conversión y formateo de tipos primitivos a texto y viceversa.
Nivel 3 (Aplicación): Implementar programas con entrada/salida interactiva robusta, purgado de buffer y validación de tipos.
Nivel 4 (Análisis): Evaluar el comportamiento ante entradas mal formadas y prevenir comportamientos indefinidos por desbordamiento.
Andamiaje Progresivo: Ejercicios andamiados con contratos formales (precondiciones/postcondiciones), tablas de vectores de prueba y suites ejecutables con
assert().
Capítulos de Apunte Correspondientes¶
Cuestiones de Estilo Aplicables¶
Inicialización de Variables: De acuerdo con la 0x7001h: Siempre debés inicializar las variables a un valor conocido, siempre se deben inicializar las variables a un valor conocido en su declaración.
Robustez en Entrada de Datos: Al utilizar
scanf, se debe validar su valor de retorno y realizar el purgado del buffer de entrada cuando sea necesario para evitar comportamientos indefinidos (ver 0x1005h: Reemplazá las condiciones ambiguas basadas en la ‘veracidad’ (truthiness) del tipo de dato).
Lectura y Escritura Básica¶
Ejercicio 1.02.01 - Declaración e Inicialización de Tipos Primitivos ⭐⭐☆☆☆¶
(ej_b1_c02_02)=
### Ejercicio 1.02.02 - Validación de Formato de Entrada ⭐⭐☆☆☆
:::{exercise}
:label: ej_b1_c02_02_entrada
Diseñá una función pura que procese una cadena de entrada simulando la lectura
de inicial, edad y promedio, validando que los tres campos se analicen
correctamente y rechazando entradas malformadas.
```c
bool parsear_datos(const char *entrada, char *inicial, int *edad, float *promedio);
```
**Tabla de Vectores de Prueba:**
| Cadena de Entrada | Retorno Esperado | Valores Parseados |
| :--- | :--- | :--- |
| `"J 20 8.75"` | `true` | `inicial='J', edad=20, promedio=8.75` |
| `"M 25"` (incompleta) | `false` | Inalterados o rechazados |
| `""` (vacía) | `false` | Retorno `false` |
::::{solution}
```c
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
bool parsear_datos(const char *entrada, char *inicial, int *edad, float *promedio) {
if (entrada == NULL || inicial == NULL || edad == NULL || promedio == NULL) {
return false;
}
int leidos = sscanf(entrada, " %c %d %f", inicial, edad, promedio);
return leidos == 3;
}
int main(void) {
char ini = ' ';
int ed = 0;
float prom = 0.0f;
assert(parsear_datos("J 20 8.75", &ini, &ed, &prom));
assert(ini == 'J');
assert(ed == 20);
assert(prom > 8.74f && prom < 8.76f);
assert(!parsear_datos("M 25", &ini, &ed, &prom));
assert(!parsear_datos("", &ini, &ed, &prom));
assert(!parsear_datos(NULL, &ini, &ed, &prom));
return 0;
}
```
(ej_b1_c02_03)=
### Ejercicio 1.02.03 - Determinación de Paridad ⭐☆☆☆☆
:::{exercise}
:label: ej_b1_c02_03_paridad
Implementá una función pura que determine si un número entero es par:
```c
bool es_par(int numero);
```
**Tabla de Vectores de Prueba:**
| Caso de Prueba | Número Entrada | Retorno Esperado | Justificación |
| :--- | :--- | :--- | :--- |
| Par positivo | `42` | `true` | `42 % 2 == 0` |
| Impar positivo | `17` | `false` | `17 % 2 != 0` |
| Cero | `0` | `true` | `0 % 2 == 0` |
| Par negativo | `-8` | `true` | `-8 % 2 == 0` |
| Impar negativo | `-9` | `false` | `-9 % 2 != 0` |
::::{solution}
```c
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
bool es_par(int numero) {
return (numero % 2) == 0;
}
int main(void) {
assert(es_par(42) == true);
assert(es_par(17) == false);
assert(es_par(0) == true);
assert(es_par(-8) == true);
assert(es_par(-9) == false);
return 0;
}
```
---
(ej_b1_c02_04)=
### Ejercicio 1.02.04 - Mayor de Tres Números ⭐⭐☆☆☆
:::{exercise}
:label: ej_b1_c02_04_mayor_tres
Implementá una función pura que determine el valor máximo entre tres números enteros:
```c
int mayor_de_tres(int a, int b, int c);
```
**Tabla de Vectores de Prueba:**
| Caso de Prueba | Entrada `(a, b, c)` | Retorno Esperado | Justificación |
| :--- | :--- | :--- | :--- |
| Primero mayor | `(30, 10, 20)` | `30` | `30 > 10` y `30 > 20` |
| Segundo mayor | `(5, 50, -2)` | `50` | `50` es el máximo |
| Tercero mayor | `(-10, -5, 0)` | `0` | `0` supera a los negativos |
| Todos iguales | `(7, 7, 7)` | `7` | Valor común |
::::{solution}
```c
#include <stdio.h>
#include <assert.h>
int mayor_de_tres(int a, int b, int c) {
int max = a;
if (b > max) {
max = b;
}
if (c > max) {
max = c;
}
return max;
}
int main(void) {
assert(mayor_de_tres(30, 10, 20) == 30);
assert(mayor_de_tres(5, 50, -2) == 50);
assert(mayor_de_tres(-10, -5, 0) == 0);
assert(mayor_de_tres(7, 7, 7) == 7);
return 0;
}
```
(ej_b1_c02_05)=
### Ejercicio 1.02.05 - Determinación de Año Bisiesto ⭐⭐☆☆☆
:::{exercise}
:label: ej_b1_c02_05_bisiesto
Implementá una función que determine si un año del calendario gregoriano es bisiesto:
- Un año es bisiesto si es divisible por 4.
- Excepto si es divisible por 100, salvo que también sea divisible por 400.
- Años menores o iguales a 0 se consideran inválidos y retornan `false`.
```c
bool es_bisiesto(int anio);
```
**Tabla de Vectores de Prueba:**
| Caso de Prueba | Año Entrada | Retorno Esperado | Regla Aplicada |
| :--- | :--- | :--- | :--- |
| Secundario bisiesto | `2024` | `true` | Divisible por 4 y no por 100 |
| Fin de siglo común | `1900` | `false` | Divisible por 100 pero no por 400 |
| Fin de siglo bisiesto | `2000` | `true` | Divisible por 400 |
| Año común | `2023` | `false` | No divisible por 4 |
| Año no positivo | `-4` | `false` | Rechazo por contrato |
::::{solution}
```c
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
bool es_bisiesto(int anio) {
if (anio <= 0) {
return false;
}
return (anio % 4 == 0 && anio % 100 != 0) || (anio % 400 == 0);
}
int main(void) {
assert(es_bisiesto(2024) == true);
assert(es_bisiesto(1900) == false);
assert(es_bisiesto(2000) == true);
assert(es_bisiesto(2023) == false);
assert(es_bisiesto(0) == false);
assert(es_bisiesto(-4) == false);
return 0;
}
```
---
(ej_b1_c02_06)=
### Ejercicio 1.02.06 - Calificación Numérica a Letra Escolar ⭐⭐☆☆☆
:::{exercise}
:label: ej_b1_c02_06_calificacion_letra
:enumerator: gradual-6
Implementá una función pura que convierta una calificación numérica porcentual en el rango $[0, 100]$ a su equivalente en escala alfabética estándar:
- $90 \le \text{nota} \le 100 \implies \text{'A'}$
- $80 \le \text{nota} < 90 \implies \text{'B'}$
- $70 \le \text{nota} < 80 \implies \text{'C'}$
- $60 \le \text{nota} < 70 \implies \text{'D'}$
- $0 \le \text{nota} < 60 \implies \text{'F'}$
- Cualquier valor fuera de $[0, 100]$ debe retornar el carácter de error `'?'`.
```c
char calificacion_a_letra(int nota);
```
**Nivel de Bloom:** Nivel 2 (Comprensión) y Nivel 3 (Aplicación).
**Conceptos requeridos:** Condicionales mutuamente excluyentes (`if-else if-else`), límites de rango defensivos.
**Techo conceptual:** Prohibido retornar códigos numéricos arbitrarios o ignorar entradas negativas / superiores a 100.
#### Contrato de la Función
- **Firma:** `char calificacion_a_letra(int nota);`
- **Precondiciones:** Ninguna (función total).
- **Postcondiciones:** Retorna `'A'`, `'B'`, `'C'`, `'D'`, `'F'`, o `'?'` ante nota inválida.
#### Tabla de Vectores de Prueba
| Caso de Prueba | Nota Entrada | Letra Retornada | Justificación Técnica |
| :--- | :--- | :--- | :--- |
| Límite superior A | `100` | `'A'` | Extremo superior válido |
| Límite inferior A | `90` | `'A'` | Umbral exacto |
| Rango intermedio B | `85` | `'B'` | Caso típico |
| Límite inferior D | `60` | `'D'` | Nota mínima de aprobación |
| Rango F | `45` | `'F'` | Desaprobado |
| Error negativo | `-5` | `'?'` | Entrada fuera de rango |
| Error desborde | `105` | `'?'` | Entrada superior al máximo |
Solution to ej_b1_c02_06_calificacion_letra
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48#include <assert.h> char calificacion_a_letra(int nota) { if (nota < 0 || nota > 100) { return '?'; } if (nota >= 90) { return 'A'; } if (nota >= 80) { return 'B'; } if (nota >= 70) { return 'C'; } if (nota >= 60) { return 'D'; } return 'F'; } int main(void) { assert(calificacion_a_letra(100) == 'A'); assert(calificacion_a_letra(90) == 'A'); assert(calificacion_a_letra(85) == 'B'); assert(calificacion_a_letra(80) == 'B'); assert(calificacion_a_letra(75) == 'C'); assert(calificacion_a_letra(60) == 'D'); assert(calificacion_a_letra(59) == 'F'); assert(calificacion_a_letra(0) == 'F'); // Casos fuera de rango assert(calificacion_a_letra(-1) == '?'); assert(calificacion_a_letra(-50) == '?'); assert(calificacion_a_letra(101) == '?'); assert(calificacion_a_letra(200) == '?'); return 0; }
Ejercicio 1.02.07 - Despachador de Operaciones Aritméticas con Validación de Dominio ⭐⭐⭐☆☆¶
---
(ej_b1_c02_08)=
## Ejercicio 1.02.08 - Triángulo Válido ⭐⭐⭐☆☆
Dados tres lados, determiná si forman un triángulo válido y qué tipo es
(equilátero, isósceles, escaleno).
**Orientación:**
- Para ser válido: a + b > c, a + c > b, b + c > a
- Equilátero: los tres lados iguales
- Isósceles: dos lados iguales
- Escaleno: todos diferentes
:::{hint} Lógica y Consideraciones
valores fuera de rango o tipos inválidos.
un lazo hasta que el usuario elija finalizar.Ejercicio 1.02.09 - Mes del Año ⭐⭐⭐☆☆¶
Dado un número de mes (1-12), mostrá su nombre y cantidad de días.
Orientación:
Usá
switchpara el mesConsiderá febrero con 28 días (ignorar bisiesto por ahora)
Manejá entradas inválidas (fuera de 1-12)
Ejercicio 1.02.10 - Signo Zodiacal ⭐⭐⭐☆☆¶
Dados día y mes de nacimiento, determiná el signo zodiacal.
Orientación:
Usá múltiples condiciones sobre mes y día
Ejemplo: Aries es del 21/03 al 19/04
Validá fechas (día válido para cada mes)
Ejercicio 1.02.11 - Ordenar Tres Números ⭐⭐⭐☆☆¶
Leé tres números y mostralos ordenados de menor a mayor.
Orientación:
Compará los tres números entre sí
Intercambiá valores si es necesario
Podés usar variables temporales
Alternativa: usar comparaciones múltiples sin intercambio
Ejercicio 1.02.12 - Vocales y Consonantes ⭐⭐⭐☆☆¶
Leé un carácter y determiná si es vocal, consonante, dígito u otro símbolo.
Orientación:
Usá
switchcon múltiples casos para vocalesVerificá si es letra con
isalpha()de<ctype.h>Verificá si es dígito con
isdigit()Considerá mayúsculas y minúsculas
Ejercicio 1.02.13 - Ecuación de Primer Grado ⭐⭐⭐☆☆¶
Resolvé ecuaciones de la forma ax + b = 0.
Casos:
Si a = 0 y b = 0: infinitas soluciones
Si a = 0 y b ≠ 0: sin solución
Si a ≠ 0: x = -b/a
Orientación:
Manejá los tres casos con
if-elseMostrá mensajes descriptivos para cada caso
Ejercicio 1.02.14 - Clasificación de IMC ⭐⭐⭐☆☆¶
Calculá el IMC y clasificá según la categoría.
Categorías:
< 18.5: Bajo peso
18.5-24.9: Normal
25-29.9: Sobrepeso
30-34.9: Obesidad I
35-39.9: Obesidad II
≥ 40: Obesidad III
Orientación:
Calculá IMC = peso / (altura²)
Usá
if-else ifpara clasificarValidá que altura > 0
Ejercicio 1.02.15 - Conversor de Calificaciones ⭐⭐⭐⭐☆¶
Convertí entre diferentes sistemas de calificación: numérica (0-10), letra (A-F), porcentaje (0-100).
Orientación:
Mostrá menú para elegir conversión
Implementá funciones de mapeo para cada conversión
Validá rangos de entrada
Ejercicio 1.02.16 - Operaciones con Fechas ⭐⭐⭐⭐☆¶
Dadas dos fechas (día, mes, año), determiná cuál es anterior.
Orientación:
Compará primero años, luego meses, finalmente días
Validá que ambas fechas sean válidas
Considerá años bisiestos para febrero
Ejercicio 1.02.17 - Calculadora de Impuestos ⭐⭐⭐⭐☆¶
Calculá impuesto a las ganancias según tramos.
Ejemplo de tramos:
10,000: 0%
30,000: 10%
60,000: 15%
$60,001+: 20%
Orientación:
Calculá impuesto acumulativo por tramo
No apliques tasa mayor al monto total, solo al excedente
Mostrá salario neto (bruto - impuesto)
Ejercicio 1.02.18 - Validador de Tarjeta de Crédito ⭐⭐⭐⭐☆¶
Implementá el algoritmo de Luhn para validar números de tarjeta.
Algoritmo:
De derecha a izquierda, duplicá cada segundo dígito
Si el resultado > 9, restá 9
Sumá todos los dígitos
Si suma % 10 = 0, es válida
Orientación:
Procesá el número dígito por dígito
Usá división y módulo para extraer dígitos
Implementá sin arrays (solo con variables)
Ejercicio 1.02.19 - Juego de Adivinanza ⭐⭐⭐⭐☆¶
El programa elige un número aleatorio entre 1 y 100. El usuario adivina y recibe pistas (“muy alto”, “muy bajo”).
Orientación:
Usá
rand()de<stdlib.h>Inicializá semilla con
srand(time(NULL))Contá intentos
Usá lazo
whilehasta acertar o límite de intentos
Ejercicio 1.02.20 - Conversor de Números Romanos ⭐⭐⭐⭐⭐¶
Convertí un número decimal (1-3999) a números romanos.
Símbolos: I=1, V=5, X=10, L=50, C=100, D=500, M=1000
Orientación:
Procesá de mayor a menor: miles, cientos, decenas, unidades
Manejá casos especiales: 4 (IV), 9 (IX), 40 (XL), 90 (XC), etc.
Usá múltiples
ifoswitchpor cada orden de magnitud
Ejercicio 1.02.21 - Calculadora de Tiempo ⭐⭐⭐⭐⭐¶
Realizá operaciones con tiempos en formato HH:MM:SS (sumar, restar, comparar).
Operaciones:
Sumar dos tiempos
Restar dos tiempos
Comparar cuál es mayor
Orientación:
Convertí todo a segundos para operar
Después convertí de vuelta a HH:MM:SS
Manejá acarreo (60 segundos = 1 minuto, etc.)
Validá formato de entrada
Ejercicio 1.02.22 - Sistema de Menú Multinivel ⭐⭐⭐⭐⭐¶
Implementá un sistema de menús con submenús.
Estructura:
MENÚ PRINCIPAL
1. Calculadora
1.1. Básica
1.2. Científica
2. Conversiones
2.1. Temperatura
2.2. Distancia
3. SalirOrientación:
Usá
switchanidadosImplementá navegación hacia atrás
Validá opciones en cada nivel
Mantené el programa en lazo hasta “Salir”
Notas Finales¶
Estas consignas cubren estructuras de control, toma de decisiones, validación de datos y lógica condicional compleja.