Actualizado el
Código de Examen C Fundamentos de programación Nivel básico Código probado
Ejercicio resuelto en C: funciones con paso por valor y por referencia (punteros), con tiempos de una marcha ciclista, código probado y errores típicos.

En una marcha cicloturista cada participante lleva un cronómetro sencillo que, en cada tramo, registra tres contadores independientes: horas, minutos y segundos. El aparato no normaliza nada, así que un tramo puede quedar anotado como 0 horas, 75 minutos y 130 segundos. La organización quiere un programa que ponga orden en esos registros.
Escribe un programa en C que lea por la entrada estándar un entero N (0 ≤ N ≤ 1000) y, a continuación, N líneas con tres enteros h m s separados por espacios, cada uno entre 0 y 10000. Cada línea es la duración de un tramo.
Debes implementar y usar obligatoriamente estas tres funciones: long long a_segundos(int h, int m, int s), que recibe los contadores por valor y devuelve la duración en segundos; void descomponer(long long total, long long *h, long long *m, long long *s), que recibe un número de segundos y deja en las tres variables apuntadas las horas, los minutos (0 a 59) y los segundos (0 a 59); y void acumular(long long seg, int indice, long long *total, long long *maximo, int *pos_max), que suma seg al total y actualiza el tramo más largo.
Salida: para cada tramo, en el orden de entrada, una línea Tramo i: HH:MM:SS, con i empezando en 1 y cada campo con al menos dos dígitos (las horas pueden tener más). Después, una línea Total: HH:MM:SS con la suma de todos los tramos y una línea Tramo mayor: k con el número del tramo más largo. Si hay empate gana el primero. Si N vale 0, el total es 00:00:00 y la última línea es Tramo mayor: ninguno.
No uses variables globales: toda la información debe circular entre funciones mediante parámetros y valores de retorno.
Entrada de ejemplo
3
0 75 130
1 0 0
0 59 61Salida esperada
Tramo 1: 01:17:10
Tramo 2: 01:00:00
Tramo 3: 01:00:01
Total: 03:17:11
Tramo mayor: 1- Función a_segundos con paso por valor, sin efectos laterales y sin desbordamiento
- 0,5
- Función descomponer que devuelve horas, minutos y segundos mediante punteros
- 0,75
- Función acumular que actualiza total, máximo y posición por referencia, con desempate correcto
- 0,75
- Programa principal: lectura, formato exacto de salida y caso sin tramos
- 0,5
Pistas
Pista 1 · ¿Qué función necesita punteros y cuál no?
Pregúntate cuántos resultados produce cada función. a_segundos produce uno solo, así que le basta con return y sus parámetros pueden ser copias. descomponer produce tres y acumular modifica variables que viven en main: ahí no hay más remedio que recibir sus direcciones.
Pista 2 · ¿Cómo se normaliza sin hacer casos especiales?
No intentes arrastrar acarreos de segundos a minutos y de minutos a horas con if. Convierte todo a segundos y luego deshaz la conversión con división entera y resto: las horas salen de dividir entre 3600, los minutos del resto de esa división dividido entre 60, y los segundos del resto entre 60.
Pista 3 · ¿Con qué valor empieza el máximo?
Un tramo puede durar 0 segundos y debe poder ser el mayor. Si inicializas maximo a -1 y pos_max a 0, cualquier tramo real supera ese máximo, y al final pos_max == 0 significa que no se leyó ninguno. Usa la comparación estricta > para que en un empate se quede el primero.
Solución
Solución explicada
La clave del ejercicio no es la aritmética, que es sencilla, sino decidir qué viaja por valor y qué por referencia. En C todos los argumentos se pasan por valor: la función recibe una copia. Cuando queremos que una función modifique una variable del llamador, lo que copiamos es su dirección, y la función escribe a través del puntero con *. Es justo lo que hace scanf cuando le pasamos &n.
La solución convierte cada tramo a segundos, lo descompone para imprimirlo normalizado y lo acumula. Al final descompone el total con la misma función, lo que demuestra que una función bien diseñada se reutiliza sin cambios.
#include <stdio.h>
/* Paso por valor: recibe copias y devuelve un unico resultado. */
static long long a_segundos(int h, int m, int s)
{
return (long long)h * 3600 + (long long)m * 60 + s;
}
/* Paso por referencia: escribe tres resultados en variables del llamador. */
static void descomponer(long long total, long long *h, long long *m, long long *s)
{
*h = total / 3600;
*m = (total % 3600) / 60;
*s = total % 60;
}
/* Paso por referencia de entrada/salida: actualiza el estado de main. */
static void acumular(long long seg, int indice, long long *total,
long long *maximo, int *pos_max)
{
*total += seg;
if (seg > *maximo) {
*maximo = seg;
*pos_max = indice;
}
}
int main(void)
{
int n;
long long total = 0;
long long maximo = -1;
int pos_max = 0;
long long hh = 0, mm = 0, ss = 0;
if (scanf("%d", &n) != 1 || n < 0) {
n = 0;
}
for (int i = 1; i <= n; i++) {
int h, m, s;
if (scanf("%d %d %d", &h, &m, &s) != 3) {
break;
}
long long seg = a_segundos(h, m, s);
descomponer(seg, &hh, &mm, &ss);
printf("Tramo %d: %02lld:%02lld:%02lld\n", i, hh, mm, ss);
acumular(seg, i, &total, &maximo, &pos_max);
}
descomponer(total, &hh, &mm, &ss);
printf("Total: %02lld:%02lld:%02lld\n", hh, mm, ss);
if (pos_max == 0) {
printf("Tramo mayor: ninguno\n");
} else {
printf("Tramo mayor: %d\n", pos_max);
}
return 0;
}a_segundos es el caso puro de paso por valor. Recibe copias de h, m y s; aunque las modificara dentro, main no se enteraría. Fíjate en el molde (long long)h * 3600: sin él, la multiplicación se haría en int y solo después se convertiría el resultado. Con los límites del enunciado un tramo cabe en int, pero el total de 1000 tramos máximos (unos 3,66·1010 segundos) no, y conviene acostumbrarse a pensar en el tipo antes de operar.
descomponer necesita devolver tres números, y return solo devuelve uno. Por eso recibe tres punteros y escribe con *h = total / 3600. En la llamada descomponer(seg, &hh, &mm, &ss) el operador & obtiene la dirección de cada variable de main; la función recibe una copia de esas direcciones, pero a través de ellas modifica los originales. En el ejemplo, el primer tramo vale 0·3600 + 75·60 + 130 = 4630 segundos, que se descomponen en 1 hora, 17 minutos y 10 segundos.
acumular combina las dos ideas: seg e indice llegan por valor porque solo se leen, mientras que total, maximo y pos_max llegan por referencia porque la función los lee y los actualiza. Es un parámetro de entrada y salida. La comparación seg > *maximo es estricta, así que en el test del empate (dos tramos de exactamente una hora, uno escrito como 0 0 3600 y otro como 1 0 0) se mantiene el tramo 1.
En main el valor inicial maximo = -1 resuelve dos problemas a la vez. Garantiza que el primer tramo siempre se registre, incluso si dura 0 segundos, como en el test de un único tramo nulo, y deja pos_max a 0 cuando no hay ningún tramo, lo que permite imprimir Tramo mayor: ninguno en el caso N = 0. Ese mismo caso imprime Total: 00:00:00 sin código especial, porque total empieza a cero.
El formato %02lld rellena con ceros hasta dos dígitos, pero no trunca: en el test de límites cada tramo da 36 610 000 segundos y se imprime como 10169:26:40. Por último, la lectura comprueba el valor de retorno de scanf; si la entrada se corta antes de tiempo, el bucle termina y se imprime lo acumulado hasta ese momento. Si quieres repasar más preguntas de este estilo, tienes una colección en ejercicios resueltos en C de preguntas de examen.
Traza paso a paso · generada ejecutando el código
| Paso | i | seg | total | maximo | pos_max |
|---|---|---|---|---|---|
| 1 | 1 | 4630 | 4630 | 4630 | 1 |
| 2 | 2 | 3600 | 8230 | 4630 | 1 |
| 3 | 3 | 3601 | 11831 | 4630 | 1 |
Casos de prueba
| Caso | Entrada | Salida esperada | Salida real | Resultado |
|---|---|---|---|---|
| Ejemplo del enunciado | 3
0 75 130
1 0 0
0 59 61 | Tramo 1: 01:17:10
Tramo 2: 01:00:00
Tramo 3: 01:00:01
Total: 03:17:11
Tramo mayor: 1 | Tramo 1: 01:17:10
Tramo 2: 01:00:00
Tramo 3: 01:00:01
Total: 03:17:11
Tramo mayor: 1 | OK |
| Sin tramos | 0 | Total: 00:00:00
Tramo mayor: ninguno | Total: 00:00:00
Tramo mayor: ninguno | OK |
| Un único tramo nulo | 1
0 0 0 | Tramo 1: 00:00:00
Total: 00:00:00
Tramo mayor: 1 | Tramo 1: 00:00:00
Total: 00:00:00
Tramo mayor: 1 | OK |
| Empate: gana el primero | 2
0 0 3600
1 0 0 | Tramo 1: 01:00:00
Tramo 2: 01:00:00
Total: 02:00:00
Tramo mayor: 1 | Tramo 1: 01:00:00
Tramo 2: 01:00:00
Total: 02:00:00
Tramo mayor: 1 | OK |
| Valores máximos | 2
10000 10000 10000
10000 10000 10000 | Tramo 1: 10169:26:40
Tramo 2: 10169:26:40
Total: 20338:53:20
Tramo mayor: 1 | Tramo 1: 10169:26:40
Tramo 2: 10169:26:40
Total: 20338:53:20
Tramo mayor: 1 | OK |
| El mayor es el último | 3
0 0 59
0 2 0
0 1 61 | Tramo 1: 00:00:59
Tramo 2: 00:02:00
Tramo 3: 00:02:01
Total: 00:05:00
Tramo mayor: 3 | Tramo 1: 00:00:59
Tramo 2: 00:02:00
Tramo 3: 00:02:01
Total: 00:05:00
Tramo mayor: 3 | OK |
Salidas reales: código compilado con gcc 14.4.0 (C17) y ejecutado en un contenedor aislado el 6 de octubre de 2026.
Complejidad
Cada tramo se procesa una sola vez con un número constante de operaciones: una conversión, una descomposición, una impresión y una actualización. El tiempo es por tanto O(N), lineal en el número de tramos, y las funciones auxiliares son O(1).
La memoria es O(1): no se guardan los tramos en un vector, porque toda la información necesaria (total, máximo y su posición) se actualiza sobre la marcha. Ese es precisamente el valor de acumular con parámetros por referencia: el estado vive en main y la función lo mantiene al día sin copiarlo.
Errores típicos
- Declarar
descomponercon parámetroslong long h, long long m, long long sen lugar de punteros: la función calcula bien, pero escribe en sus copias locales ymainimprime los valores que tuvieran antes sus variables. - Olvidar el asterisco dentro de la función y escribir
h = total / 3600conhpuntero: se cambia la dirección guardada en la copia del puntero, no el dato apuntado, y el compilador avisa de una conversión de entero a puntero. - Llamar a
descomponer(seg, hh, mm, ss)sin el operador&: se pasan valores donde se esperan direcciones; el programa no compila limpio y, si se fuerza, suele terminar en violación de segmento. - Inicializar
maximoa 0 y comparar con>: un único tramo de duración 0 nunca se registra y el programa respondeningunocuando debería responder 1. - Usar
>=en la comparación del máximo: con tramos empatados gana el último, contradiciendo el enunciado. - Calcular en
intsin moldes nilong long: con muchos tramos largos el total desborda y aparecen horas negativas.
Variantes
Normalizar en el sitio sin pasar por segundos
El profesor puede pedir void normalizar(int *h, int *m, int *s), que modifique directamente los tres contadores. Se resuelve con acarreos: *m += *s / 60; *s %= 60; *h += *m / 60; *m %= 60;. El orden importa: primero se pasan los segundos sobrantes a minutos y después los minutos a horas. Aquí los tres parámetros son a la vez de entrada y de salida.
Devolver una estructura en lugar de usar punteros
Otra forma de devolver varios valores es definir struct duracion { long long h, m, s; }; y escribir struct duracion descomponer(long long total), que construye la estructura y la devuelve por valor. Es más legible y evita punteros nulos, pero copia la estructura en cada llamada; con tres campos el coste es irrelevante. Conviene saber defender ambas opciones en el examen.
Intercambiar los dos tramos más largos
Una pregunta clásica es añadir void intercambiar(long long *a, long long *b) con una variable auxiliar: long long t = *a; *a = *b; *b = t;. Sirve para comprobar que el alumno entiende que una versión sin punteros intercambia solo las copias y deja intactas las variables originales.
Historial de cambios
- Reescrito con la plantilla de Código de Examen: enunciado de examen, pistas, solución explicada, casos de prueba y código compilado y ejecutado; versión en inglés enlazada.
- Publicación original.
Elaborado con apoyo de IA y revisado por la redacción · Revisado por Jose Santamaria



Comentarios