Fundamentos de Programación en la universidad: qué entra, 5 ejercicios de examen resueltos en C y cómo aprobarla

Fundamentos de Programación (también llamada Programación I o Introducción a la Programación) es la asignatura de primer curso de Ingeniería Informática y grados afines en la que aprendes a programar desde cero: variables, condicionales, bucles, funciones, arrays, cadenas y recursividad, casi siempre en C, C++, Java o Python. Para aprobarla tienes que escribir sin ayuda del IDE cinco tipos de ejercicio (bucles anidados, funciones con paso por valor y por referencia, recorridos de arrays, cadenas y recursividad) y saber seguir a mano lo que hace un programa.
Lo que la hace difícil no es el temario, que cabe en una hoja. Es que va muy rápido, que el examen suele ser en papel o con un corrector automático que no perdona una coma y que, si es en C, el programa puede compilar y hacer algo raro sin avisarte. La visión general de la carrera la tienes en las asignaturas más difíciles de Ingeniería Informática. Aquí vamos al detalle: qué entra, cómo trazar un programa en papel, cinco ejercicios tipo examen resueltos en C (con su equivalente en Python cuando aporta), los errores que más vemos en clase y cómo prepararla.
Qué entra en Fundamentos de Programación
El nombre, el lenguaje y el reparto de temas cambian según la universidad, pero el contenido es muy parecido en toda España:
| Bloque | Qué te piden | Lo que más falla |
|---|---|---|
| Tipos, variables y expresiones | Declarar, leer y escribir datos, operadores, división entera y resto | Mezclar int y double y perder decimales |
| Estructuras de control | if, switch, while, for, bucles anidados |
Condiciones de parada y bucles que dan una vuelta de más o de menos |
| Funciones | Parámetros, valor de retorno, paso por valor y por referencia | Imprimir dentro de la función en vez de devolver el resultado |
| Arrays y matrices | Recorrer, buscar, contar, invertir, ordenar | Salirse del array por una posición |
| Cadenas | Recorrer carácter a carácter, contar, comparar | Olvidar el '\0' final o comparar cadenas con == |
| Registros (struct) | Agrupar datos, arrays de registros | No saber cuándo pasar el registro por referencia |
| Recursividad | Caso base y caso recursivo, trazar llamadas | Recursión sin caso base o que no se acerca a él |
Algunos ejemplos reales, según sus guías docentes recientes:
- UNED: Fundamentos de Programación se da en C±, un subconjunto de C y C++ pensado para aprender. El examen no permite material y combina preguntas tipo test con un ejercicio de programación.
- Universidad de Granada: Fundamentos de Programación se da en C++. La teoría se evalúa con un examen escrito y la práctica con un examen en el ordenador.
- UPC (FIB): la asignatura se llama Programació 1 (PRO1) y se da en C++. Las prácticas y los exámenes se corrigen en Jutge.org, un juez automático.
- UPM (ETSI Informáticos): Programación I usa un subconjunto de Java.
Ojo si buscas "examen fundamentos de programación UPC": muchos resultados son de la Universidad Peruana de Ciencias Aplicadas, que comparte siglas.
Abre la guía docente de tu asignatura y mira el lenguaje, el formato del examen y cuánto pesan las prácticas. Los ejercicios de abajo están en C porque es el caso más exigente: si te salen en C, en C++, Java o Python te costarán menos.
Paso por valor y por referencia: lo que siempre preguntan
Paso por valor significa que la función recibe una copia del dato: si la modifica, el original no cambia. Paso por referencia significa que la función recibe la dirección del dato original, así que lo que cambie dentro se ve fuera. En C todo se pasa por valor, y para simular el paso por referencia se pasa un puntero (int *p) y se llama con &variable. En C++ existe el paso por referencia real con int &x. En Java y Python no eliges: los números se comportan como si fueran por valor y los objetos se pueden modificar desde dentro de la función.
Por eso scanf necesita el & y por eso los arrays sí se modifican desde una función. Es la base del ejercicio 2.
Cómo leer un enunciado y trazar el programa en papel
En el examen casi siempre hay dos tipos de pregunta: "escribe un programa que..." y "¿qué muestra este código?". Las dos se entrenan con papel y sin ordenador.
Para escribir un programa a partir del enunciado:
- Subraya entradas, salidas y formato exacto. Con un juez automático, un espacio de más es un error.
- Resuelve un ejemplo pequeño a mano. Si no sabes calcularlo tú, no sabrás programarlo.
- Decide la estructura: ¿número de repeticiones conocido?
for. ¿Hasta que pase algo?while. ¿Una repetición dentro de otra? Bucle anidado. - Escribe primero la cabecera de cada función (qué recibe y qué devuelve) y después el cuerpo.
- Comprueba los casos límite: 0, 1, negativo, array vacío, cadena vacía.
Para trazar un programa se usa una tabla con una columna por variable y una fila por cada cambio. Por ejemplo:
int s = 0;
for (int i = 1; i <= 4; i++) {
if (i % 2 == 0) {
s += i;
} else {
s -= i;
}
}
printf("%d\n", s);
| i | ¿i par? | s |
|---|---|---|
| (inicio) | 0 | |
| 1 | no | -1 |
| 2 | sí | 1 |
| 3 | no | -2 |
| 4 | sí | 2 |
| 5 | sale del bucle | 2 |
El programa muestra 2. Lo que más falla es saltarse filas por ir rápido. Escribe todas, también la última comprobación de la condición.
Si tu asignatura usa un juez automático como Jutge, entrena ese formato: la salida tiene que coincidir carácter a carácter. En cómo preparar un examen práctico de programación tienes cómo organizarte para ese tipo de prueba.
5 ejercicios de examen de Fundamentos de Programación resueltos en C
Intenta hacer cada uno antes de mirar la solución. Todos compilan sin avisos con gcc -Wall -Wextra, y las salidas que ves son las reales.
1. Bucles anidados: dibujar una pirámide
Enunciado: lee un número entero positivo n y dibuja una pirámide de asteriscos de altura n. Para n = 5, la salida es la de abajo.
*
***
*****
*******
*********
#include <stdio.h>
int main(void) {
int n;
printf("Altura: ");
if (scanf("%d", &n) != 1 || n <= 0) {
printf("Altura no valida\n");
return 1;
}
for (int fila = 1; fila <= n; fila++) {
for (int i = 0; i < n - fila; i++) { /* espacios de la izquierda */
printf(" ");
}
for (int i = 0; i < 2 * fila - 1; i++) { /* asteriscos */
printf("*");
}
printf("\n");
}
return 0;
}
Lo que hay que entender: el bucle de fuera recorre las filas y dentro hay dos bucles seguidos (no uno dentro del otro): espacios y luego asteriscos. La clave es la fórmula de cuántos de cada hay por fila. Hazte una tabla: fila 1, 4 espacios y 1 asterisco; fila 2, 3 y 3; fila 3, 2 y 5. De ahí salen n - fila y 2 * fila - 1. Quien intenta adivinar las fórmulas probando código pierde el examen; con la tabla las tienes en un minuto.
En Python cabe en una línea por fila, porque un texto se puede multiplicar por un número:
for fila in range(1, n + 1):
print(" " * (n - fila) + "*" * (2 * fila - 1))
2. Paso por valor frente a punteros: intercambiar y dividir
Enunciado: escribe una función que intercambie el valor de dos variables enteras y otra que, dados un dividendo y un divisor, devuelva a la vez el cociente y el resto.
#include <stdio.h>
void intercambiar_mal(int a, int b) {
int aux = a;
a = b;
b = aux;
}
void intercambiar(int *a, int *b) {
int aux = *a;
*a = *b;
*b = aux;
}
/* Devuelve dos resultados a la vez: cociente y resto */
void dividir(int dividendo, int divisor, int *cociente, int *resto) {
*cociente = dividendo / divisor;
*resto = dividendo % divisor;
}
int main(void) {
int x = 3, y = 7;
intercambiar_mal(x, y);
printf("Por valor: x = %d, y = %d\n", x, y);
intercambiar(&x, &y);
printf("Por referencia: x = %d, y = %d\n", x, y);
int c, r;
dividir(17, 5, &c, &r);
printf("17 / 5 = %d y sobran %d\n", c, r);
return 0;
}
Salida:
Por valor: x = 3, y = 7
Por referencia: x = 7, y = 3
17 / 5 = 3 y sobran 2
Lo que hay que entender: intercambiar_mal intercambia sus copias y, al terminar, las copias desaparecen; x e y siguen igual. La versión correcta recibe direcciones (int *a), se llama con &x y trabaja sobre *a, que es "lo que hay en esa dirección". Así se devuelve también más de un resultado: return solo da un valor y los demás salen por punteros. La trampa típica en papel es escribir a = b en vez de *a = *b: copia direcciones y no cambia nada fuera.
En Python no hay punteros a enteros. Lo idiomático es devolver varios valores y asignarlos:
def dividir(dividendo, divisor):
return dividendo // divisor, dividendo % divisor
c, r = dividir(17, 5) # c = 3, r = 2
x, y = y, x # intercambio directo
3. Arrays: máximo, contar e invertir
Enunciado: dado un array de enteros, escribe funciones que devuelvan la posición del máximo, cuenten cuántas veces aparece un valor e inviertan el array sin usar otro auxiliar.
#include <stdio.h>
#define TAM 8
int posicion_maximo(const int v[], int n) {
int pos = 0;
for (int i = 1; i < n; i++) {
if (v[i] > v[pos]) {
pos = i;
}
}
return pos;
}
int contar(const int v[], int n, int x) {
int veces = 0;
for (int i = 0; i < n; i++) {
if (v[i] == x) {
veces++;
}
}
return veces;
}
void invertir(int v[], int n) {
for (int i = 0, j = n - 1; i < j; i++, j--) {
int aux = v[i];
v[i] = v[j];
v[j] = aux;
}
}
void mostrar(const int v[], int n) {
for (int i = 0; i < n; i++) {
printf("%d ", v[i]);
}
printf("\n");
}
int main(void) {
int v[TAM] = {4, -2, 9, 4, 0, 9, 7, 4};
int pm = posicion_maximo(v, TAM);
printf("Maximo: %d (posicion %d)\n", v[pm], pm);
printf("El 4 aparece %d veces\n", contar(v, TAM, 4));
invertir(v, TAM);
printf("Invertido: ");
mostrar(v, TAM);
return 0;
}
Salida:
Maximo: 9 (posicion 2)
El 4 aparece 3 veces
Invertido: 4 7 9 0 4 9 -2 4
Lo que hay que entender: el máximo se inicializa con el primer elemento, no con 0: si todos fueran negativos, saldría un 0 que no está en el array. Con > te quedas con la primera aparición del máximo; con >=, con la última. Para invertir, dos índices que se acercan y paran cuando se cruzan (i < j): si recorres el array entero, lo inviertes dos veces y queda igual. Por último, el tamaño se pasa siempre como parámetro: dentro de la función, v es un puntero y sizeof(v) no te da el número de elementos.
4. Cadenas: contar palabras y detectar palíndromos
Enunciado: lee una frase y muestra cuántas palabras tiene (puede haber varios espacios seguidos) y si es un palíndromo, es decir, si se lee igual al derecho que al revés.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int contar_palabras(const char s[]) {
int palabras = 0;
int dentro = 0; /* 1 si estamos dentro de una palabra */
for (int i = 0; s[i] != '\0'; i++) {
if (isspace((unsigned char) s[i])) {
dentro = 0;
} else if (!dentro) {
dentro = 1;
palabras++;
}
}
return palabras;
}
int es_palindromo(const char s[]) {
int i = 0;
int j = (int) strlen(s) - 1;
while (i < j) {
if (s[i] != s[j]) {
return 0;
}
i++;
j--;
}
return 1;
}
int main(void) {
char linea[200];
printf("Frase: ");
if (fgets(linea, sizeof linea, stdin) == NULL) {
return 1;
}
linea[strcspn(linea, "\n")] = '\0'; /* quita el salto de linea */
printf("Palabras: %d\n", contar_palabras(linea));
printf("%s\n", es_palindromo(linea) ? "Es palindromo" : "No es palindromo");
return 0;
}
Con " hola que tal " cuenta 3 palabras; con anilina o radar, 1 palabra y palíndromo.
Lo que hay que entender: contar espacios y sumar uno falla en cuanto hay dos espacios seguidos o espacios al principio. Lo robusto es contar inicios de palabra: un carácter que no es espacio cuando estabas fuera de una palabra. En C una cadena es un array de char que termina en '\0', y por eso el bucle para ahí y no en el tamaño del array. fgets guarda también el salto de línea del final; si no lo quitas, radar deja de ser palíndromo porque compara r con \n. Si el enunciado pide ignorar espacios y mayúsculas, primero copia solo las letras pasadas a minúscula con tolower y luego compara.
En Python las dos cosas son casi gratis, y por eso es habitual que en el examen te pidan hacerlas a mano:
len(frase.split()) # número de palabras
texto == texto[::-1] # ¿palíndromo?
5. Recursividad: suma de dígitos y potencia
Enunciado: escribe una función recursiva que sume los dígitos de un número natural y otra que calcule la potencia base^exp con exp >= 0, sin usar bucles.
#include <stdio.h>
int suma_digitos(int n) {
if (n < 10) { /* caso base: un solo digito */
return n;
}
return n % 10 + suma_digitos(n / 10);
}
long potencia(int base, int exp) {
if (exp == 0) { /* caso base */
return 1;
}
return base * potencia(base, exp - 1);
}
int main(void) {
printf("suma_digitos(4072) = %d\n", suma_digitos(4072));
printf("potencia(2, 10) = %ld\n", potencia(2, 10));
printf("potencia(5, 0) = %ld\n", potencia(5, 0));
return 0;
}
Salida: 13, 1024 y 1.
Lo que hay que entender: toda función recursiva responde a dos preguntas. ¿Cuál es el caso que sé resolver sin recursión? (el caso base: un número de una cifra, o exponente 0). ¿Cómo reduzco el problema a uno más pequeño? (quitar la última cifra con n / 10, bajar el exponente en 1). La traza en papel se escribe hacia abajo y se resuelve hacia arriba:
suma_digitos(4072) = 2 + suma_digitos(407)
= 2 + (7 + suma_digitos(40))
= 2 + (7 + (0 + suma_digitos(4)))
= 2 + 7 + 0 + 4 = 13
La trampa típica: con un exponente negativo, potencia nunca llega a 0 y el programa se cae por desbordamiento de pila. Por eso el enunciado dice exp >= 0. Fibonacci sigue el mismo esquema con dos casos base (0 y 1) y dos llamadas; la versión recursiva ingenua es muy lenta porque recalcula los mismos valores muchas veces.
Los errores que más suspenden en Fundamentos de Programación
= en lugar de ==. if (x = 5) no compara: asigna 5 a x y la condición siempre es verdadera.
Salirse del array por una posición (off-by-one). Un array de 8 elementos va de v[0] a v[7]. Con i <= n en lugar de i < n lees una posición que no es tuya, y C no te avisa.
Punto y coma detrás de if o for. for (i = 0; i < n; i++); ejecuta un bucle vacío y luego el bloque de llaves una sola vez. Con if (x > 0); el bloque se ejecuta siempre.
Variables sin inicializar. int suma; empieza con un valor cualquiera. Contadores y acumuladores se inicializan antes del bucle.
scanf sin &. scanf("%d", n) le pasa a scanf el valor de n en vez de su dirección, y escribe en una posición de memoria cualquiera. Con cadenas (char nombre[50]) no se pone &, porque el nombre del array ya es una dirección.
Cadenas sin '\0'. Si construyes una cadena carácter a carácter sin el terminador, printf y strlen siguen leyendo memoria hasta encontrar un cero. Y las cadenas se comparan con strcmp, no con ==.
Devolver un puntero a una variable local. Una función que declara char texto[20] y hace return texto; devuelve la dirección de algo que deja de existir al salir. A veces parece funcionar, que es lo peor que puede pasar.
Recursión sin caso base o que no se acerca a él. Si la llamada recursiva no reduce el problema, o el caso base no cubre todos los finales posibles, el programa no termina.
Imprimir en lugar de devolver. Si el enunciado pide "una función que calcule el máximo", tiene que hacer return, no printf. Si imprime, no se puede reutilizar y en un juez automático esa salida sobra.
Muchos de estos los detecta el compilador si compilas con gcc -Wall -Wextra: la asignación en un if, el if con cuerpo vacío, el scanf sin &, variables sin inicializar o la dirección de una variable local devuelta. Lee los avisos, no solo los errores. Y si el fallo no avisa, en cómo depurar código paso a paso tienes un método que sirve para cualquier lenguaje. Si tu asignatura es en Python, los equivalentes los tienes en los errores más comunes en Python para principiantes.
Cómo preparar el examen de Fundamentos de Programación
Programa un poco cada día. Media hora diaria escribiendo código vale más que una tarde de domingo leyendo apuntes.
Practica en el formato del examen. Si es en papel, escribe en papel desde la tercera semana. Si es con juez automático, envía allí las prácticas.
Rehaz los ejercicios sin mirar. Los de este artículo, las prácticas y los exámenes de otros años. El punto donde te quedas en blanco es lo que tienes que estudiar.
Traza código ajeno. Antes de ejecutar un ejercicio resuelto, escribe la tabla de variables y la salida que esperas, y luego compruébalo.
Prueba los casos límite. 0, 1, un negativo, el array o la cadena vacía y el caso más grande que permita el enunciado. Ahí se pierden muchos puntos.
Si nunca has programado, empezar antes de septiembre te da margen: tienes una ruta en cómo aprender programación desde cero. Y ten en cuenta que esta asignatura es la base de la siguiente: en Estructuras de Datos en la universidad vas a necesitar punteros, structs y recursividad con soltura.
Cuándo pedir ayuda
Si vas al día con las prácticas y te salen los ejercicios sin mirar, vas bien. Merece la pena buscar ayuda cuando:
- Entiendes las clases, pero delante de un enunciado en blanco no sabes por dónde empezar.
- Tu programa compila, da un resultado raro y no sabes ni dónde buscar.
- Los punteros o la recursividad te parecen magia y el resto del temario se apoya en ellos.
- Has suspendido la primera convocatoria y tienes la segunda en pocas semanas.
En nuestras clases particulares de programación para universidad trabajamos con tu guía docente, tus prácticas y exámenes de otros años: trazamos en papel, buscamos dónde se rompe el razonamiento y lo resuelves tú. Si tu asignatura es en C o C++, puedes ir directo a las clases de C y C++ online; si es en Python, a las clases de Python online. Son clases online uno a uno a 25 € la hora, sin matrícula ni permanencia.
Preguntas frecuentes
¿Qué es Fundamentos de Programación?
Es la asignatura de primer curso de Ingeniería Informática y otros grados de ingeniería en la que se aprende a programar desde cero: variables, condicionales, bucles, funciones, arrays, cadenas, registros y recursividad. También se llama Programación I o Introducción a la Programación.
¿Qué lenguaje se usa en Fundamentos de Programación?
Depende de la universidad: la UNED usa C± (un subconjunto de C y C++), la Universidad de Granada y la FIB de la UPC usan C++, la UPM usa un subconjunto de Java y otras usan Python. Mira la guía docente y practica en ese lenguaje.
¿Es difícil Fundamentos de Programación?
Cuesta a mucha gente en primero, sobre todo a quien no había programado antes, porque el ritmo es alto y el examen se hace sin ayudas. El contenido en sí no es complicado: quien programa cada semana y practica en el formato del examen suele aprobarla.
¿Cómo es el examen de Fundamentos de Programación de la UNED?
Según la guía del curso 2026/27, es un examen presencial de 120 minutos sin material, con preguntas tipo test eliminatorias y un ejercicio de programación en C±. Las prácticas (PEC) son voluntarias, pero si las apruebas te piden menos aciertos en el test. Revisa la guía de tu curso porque los criterios pueden cambiar.
¿Cómo es un examen parcial o final de Fundamentos de Programación?
Suele combinar preguntas de traza ("¿qué muestra este código?"), preguntas cortas de teoría como la diferencia entre paso por valor y por referencia, y uno o varios ejercicios de escribir funciones con bucles, arrays, cadenas o recursividad. Puede ser en papel, en ordenador o con un juez automático, y el parcial suele cubrir solo la primera parte del temario.
¿Puedo estudiar Fundamentos de Programación con Python si en clase usan C?
Para entender la lógica sí ayuda, porque Python tiene menos sintaxis que distraiga. Pero en C hay cosas que en Python no existen, como los punteros, el '\0' de las cadenas o el tamaño fijo de los arrays, y el examen será en el lenguaje de la asignatura. Practica sobre todo en ese.