42 Piscine · Guía de Estudio · Parte 2

C04 · C05 · C06
Guía Completa

Conversión de bases · Recursión · argc/argv — Ejercicios paso a paso con código comentado

C04 · atoi / bases C05 · recursión / matemáticas C06 · argc / argv
C 04

C 04 — Conversión numérica y bases

Concepto clave: Este módulo trabaja la conversión entre tipos (string → int con ft_atoi) y la representación de números en distintas bases. En base N, cada dígito vale su posición multiplicada por N elevado a su índice. Binario=base 2, Octal=base 8, Decimal=base 10, Hex=base 16.
EX 03 ft_atoi — CLÁSICO DE EXAMEN ★★★☆ Difícil
int ft_atoi(char *str);

Convierte un string en int. Maneja espacios al inicio, signos + y - múltiples (los negativos cambian el signo según paridad), y dígitos hasta encontrar un no-dígito.

Ejemplo: " ---+--+1234ab567"-1234 (3 negativos = signo negativo, se para en 'a').

→ Razonamiento paso a paso (3 fases)
  1. Fase 1 — Espacios: Salta todos los caracteres que son espacio: ' ', '\t', '\n', '\r', '\f', '\v' (los 6 que define isspace).
  2. Fase 2 — Signos: Lee + y -. Cada - invierte el signo. Usa una variable sign = 1 y multiplica por -1 cada vez que encuentras un -.
  3. Fase 3 — Dígitos: Mientras el char es dígito ('0''9'), construye el número: result = result * 10 + (str[i] - '0').
  4. Devuelve result * sign.
ft_atoi.c
int	ft_atoi(char *str)
{
	int	i;
	int	sign;
	int	result;

	i = 0;
	sign = 1;
	result = 0;
	while (str[i] == ' ' || (str[i] >= '\t' && str[i] <= '\r'))
		i++;
	while (str[i] == '+' || str[i] == '-')
	{
		if (str[i] == '-')
			sign = sign * -1;
		i++;
	}
	while (str[i] >= '0' && str[i] <= '9')
	{
		result = result * 10 + (str[i] - '0');
		i++;
	}
	return (result * sign);
}
💡 La condición str[i] >= '\t' && str[i] <= '\r' captura \t \n \v \f \r de una vez porque son ASCII 9–13 consecutivos.
⚠️ str[i] - '0' convierte el carácter dígito en su valor numérico: '5' - '0' = 53 - 48 = 5.
EX 04 ft_putnbr_base — MUY FRECUENTE EN EXAMEN ★★★☆ Difícil
void ft_putnbr_base(int nbr, char *base);

Muestra nbr en la base dada. La base es un string donde cada carácter es un símbolo. Si la base es inválida (vacía, tamaño 1, contiene +, -, o caracteres repetidos), no muestra nada.

🎯 Idea clave: Si la base es "0123456789ABCDEF" (tamaño 16), base[nbr % 16] da el dígito hexadecimal. Recursivamente imprime nbr / base_len primero, luego el dígito actual.
→ Pasos
  1. Calcula base_len: recorre la base hasta '\0'.
  2. Valida la base: si len < 2, o contiene '+'/'-', o tiene duplicados → return.
  3. Gestiona negativos: si nbr < 0 imprime '-' y niega nbr (con cuidado al INT_MIN).
  4. Si nbr >= base_len, llama recursivamente con nbr / base_len.
  5. Imprime base[nbr % base_len] — el dígito actual.
ft_putnbr_base.c
#include <unistd.h>

static int	ft_base_len(char *base)
{
	int	i;
	int	j;

	i = 0;
	while (base[i])
	{
		if (base[i] == '+' || base[i] == '-')
			return (0);
		j = i + 1;
		while (base[j])
		{
			if (base[i] == base[j])
				return (0);
			j++;
		}
		i++;
	}
	return (i);
}

void	ft_putnbr_base(int nbr, char *base)
{
	int	len;
	char	c;

	len = ft_base_len(base);
	if (len < 2)
		return ;
	if (nbr == -2147483648)
	{
		ft_putnbr_base(-214748364, base);
		c = base[8 % len];
		write(1, &c, 1);
		return ;
	}
	if (nbr < 0)
	{
		write(1, "-", 1);
		nbr = -nbr;
	}
	if (nbr >= len)
		ft_putnbr_base(nbr / len, base);
	c = base[nbr % len];
	write(1, &c, 1);
}
🚨 El INT_MIN necesita tratamiento especial también en base. Aquí: -2147483648 / base da -214748364 y el dígito restante es 8 (en decimal). Se adapta al índice de la base.
EX 05 ft_atoi_base ★★★★ Muy difícil
int ft_atoi_base(char *str, char *base);

Como ft_atoi pero convierte desde una base específica. Mismo comportamiento de signos y espacios. Si la base es inválida devuelve 0. No hay espacios en la base (a diferencia de ft_putnbr_base, aquí los espacios en la base también son error).

→ Idea clave: buscar cada carácter de str en la base
  1. Valida la base igual que en ft_putnbr_base (más: no puede contener espacios).
  2. Salta espacios al inicio del string.
  3. Lee signos + y - acumulando el signo.
  4. Para cada carácter de str, busca su índice en la base. Ese índice es su valor.
  5. Construye el número: result = result * base_len + indice_en_base.
ft_atoi_base.c
static int	ft_find_in_base(char c, char *base)
{
	int	i;

	i = 0;
	while (base[i])
	{
		if (base[i] == c)
			return (i);
		i++;
	}
	return (-1);  // no encontrado
}

int	ft_atoi_base(char *str, char *base)
{
	int	i;
	int	len;
	int	sign;
	int	result;
	int	val;

	len = ft_base_len(base);  // misma función de antes
	if (len < 2)
		return (0);
	i = 0;
	sign = 1;
	result = 0;
	while (str[i] == ' ' || (str[i] >= '\t' && str[i] <= '\r'))
		i++;
	while (str[i] == '+' || str[i] == '-')
	{
		if (str[i] == '-')
			sign *= -1;
		i++;
	}
	val = ft_find_in_base(str[i], base);
	while (val != -1)
	{
		result = result * len + val;
		i++;
		val = ft_find_in_base(str[i], base);
	}
	return (result * sign);
}
C 05

C 05 — Recursión y matemáticas

Concepto clave — Recursión: Una función recursiva se llama a sí misma. Necesita siempre un caso base (condición de parada) y un caso recursivo (se acerca al caso base). Sin caso base → recursión infinita → stack overflow. En C, no puedes usar bucles donde se pide recursión.
patron_recursion.c
// Patrón general de función recursiva:
int	funcion_recursiva(int n)
{
	if (n == CASO_BASE)           // ← condición de parada
		return (VALOR_BASE);
	return (funcion_recursiva(n - 1) * n); // ← llamada recursiva
}
EX 00 ft_iterative_factorial ★★☆☆ Medio
int ft_iterative_factorial(int nb);

Factorial iterativo: n! = 1×2×3×...×n. Si nb < 0 devuelve 0. 0! = 1.

ft_iterative_factorial.c
int	ft_iterative_factorial(int nb)
{
	int	result;

	if (nb < 0)
		return (0);
	result = 1;
	while (nb > 1)
	{
		result = result * nb;
		nb--;
	}
	return (result);
}
💡 Empezamos con result = 1 porque 1 es el neutro de la multiplicación. El loop empieza desde nb y va bajando hasta 2. Con nb=0 o nb=1 el while no se ejecuta y devuelve 1, que es correcto (0! = 1! = 1).
EX 01 ft_recursive_factorial ★★☆☆ Medio
int ft_recursive_factorial(int nb);

Factorial recursivo. Misma lógica pero con llamadas a sí mismo. n! = n × (n-1)!

ft_recursive_factorial.c
int	ft_recursive_factorial(int nb)
{
	if (nb < 0)
		return (0);
	if (nb <= 1)
		return (1);   // caso base: 0! = 1! = 1
	return (nb * ft_recursive_factorial(nb - 1));
}
// 5! → 5 × 4! → 5 × 4 × 3! → ... → 5×4×3×2×1 = 120
EX 02 / 03 ft_iterative_power / ft_recursive_power ★★☆☆ Medio
int ft_iterative_power(int nb, int power); int ft_recursive_power(int nb, int power);

Calcula nbpower. Si power < 0 devuelve 0. 00 = 1 (decisión del enunciado).

iterativa
int	ft_iterative_power(int nb, int power)
{
	int	result;

	if (power < 0)
		return (0);
	result = 1;
	while (power > 0)
	{
		result = result * nb;
		power--;
	}
	return (result);
}
recursiva
int	ft_recursive_power(int nb, int power)
{
	if (power < 0)
		return (0);
	if (power == 0)
		return (1);
	return (nb * ft_recursive_power(
			nb, power - 1));
}
EX 04 ft_fibonacci — CLÁSICO DE EXAMEN ★★★☆ Difícil
int ft_fibonacci(int index);

Devuelve el elemento en la posición index de la secuencia de Fibonacci: 0, 1, 1, 2, 3, 5, 8, 13, 21... Si index < 0 devuelve -1. Debe ser recursiva.

→ Casos base: fib(0) = 0, fib(1) = 1. Caso recursivo: fib(n) = fib(n-1) + fib(n-2)
ft_fibonacci.c
int	ft_fibonacci(int index)
{
	if (index < 0)
		return (-1);
	if (index == 0)
		return (0);   // fib(0) = 0
	if (index == 1)
		return (1);   // fib(1) = 1
	return (ft_fibonacci(index - 1) + ft_fibonacci(index - 2));
}
// fib(5) → fib(4) + fib(3) → ... → 3 + 2 = 5 ✓
⚠️ Esta implementación tiene complejidad O(2^n) — muy lenta para valores grandes. Pero en el examen de 42 no lo piden eficiente, solo correcto.
EX 05 ft_sqrt ★★☆☆ Medio
int ft_sqrt(int nb);

Devuelve la raíz cuadrada entera de nb si es exacta, 0 si no. Ej: sqrt(25)=5, sqrt(26)=0.

ft_sqrt.c
int	ft_sqrt(int nb)
{
	int	i;

	if (nb < 0)
		return (0);
	if (nb == 0)
		return (0);
	i = 1;
	while (i * i <= nb)
	{
		if (i * i == nb)
			return (i);
		i++;
	}
	return (0);
}
💡 La condición i * i <= nb para antes de que i supere la raíz real. Si ningún i exactamente satisface i*i == nb, devuelves 0.
EX 06 ft_is_prime ★★☆☆ Medio
int ft_is_prime(int nb);

Devuelve 1 si nb es primo, 0 si no. 0 y 1 no son primos. Un número es primo si solo es divisible entre 1 y él mismo.

→ Truco: solo hay que probar divisores hasta √nb
ft_is_prime.c
int	ft_is_prime(int nb)
{
	int	i;

	if (nb <= 1)
		return (0);
	i = 2;
	while (i * i <= nb)
	{
		if (nb % i == 0)
			return (0);  // tiene divisor → no es primo
		i++;
	}
	return (1);
}
EX 07 ft_find_next_prime ★★☆☆ Medio
int ft_find_next_prime(int nb);

Devuelve el primer número primo ≥ nb. Si nb ya es primo, devuelve nb.

ft_find_next_prime.c
int	ft_find_next_prime(int nb)
{
	if (nb <= 2)
		return (2);
	while (!ft_is_prime(nb))
		nb++;
	return (nb);
}
// Reutiliza ft_is_prime — la clave de C05
💡 Este ejercicio demuestra la importancia de reutilizar funciones. Necesitas tener ft_is_prime en el mismo archivo o declarada como static arriba.
C 06

C 06 — argc y argv

Concepto clave — argc / argv: Cuando ejecutas un programa en terminal, los argumentos llegan a main. argc es el número de argumentos (incluido el nombre del programa). argv es un array de strings: argv[0] es el nombre del programa, argv[1] es el primer argumento, etc.

Ejemplo: ./programa hola mundo

argc = 3
argv[0] → "./programa"
argv[1] → "hola"
argv[2] → "mundo"
argv[3] → NULL (siempre termina en NULL)
⚠️ En C06 los ejercicios piden programas, no funciones. Necesitas int main(int argc, char **argv). Puedes usar ft_putstr que ya escribiste en C01/C04.
EX 00 ft_print_program_name ☆☆☆ Básico

Programa que imprime el nombre con el que fue ejecutado (argv[0]) seguido de un salto de línea.

ft_print_program_name.c
#include <unistd.h>

static void	ft_putstr(char *str)
{
	int	i;

	i = 0;
	while (str[i])
	{
		write(1, &str[i], 1);
		i++;
	}
}

int	main(int argc, char **argv)
{
	(void)argc;   // evita warning de variable no usada
	ft_putstr(argv[0]);
	write(1, "\n", 1);
	return (0);
}
💡 (void)argc; silencia el warning de la Norma por variable sin usar. Es la forma idiomática de C para decir "este parámetro es intencionalmente ignorado".
EX 01 ft_print_params ☆☆☆ Básico

Imprime todos los argumentos excepto argv[0], uno por línea, en el mismo orden que se recibieron.

ft_print_params.c
int	main(int argc, char **argv)
{
	int	i;

	i = 1;   // empieza en 1 para saltar argv[0]
	while (i < argc)
	{
		ft_putstr(argv[i]);
		write(1, "\n", 1);
		i++;
	}
	return (0);
}
EX 02 ft_rev_params ★★☆☆ Medio

Igual que el anterior pero en orden inverso. Si se ejecuta con test1 test2 test3, imprime test3, test2, test1.

→ Empieza desde argc-1 y baja hasta 1
ft_rev_params.c
int	main(int argc, char **argv)
{
	int	i;

	i = argc - 1;   // último argumento
	while (i >= 1)  // hasta 1 (saltamos argv[0])
	{
		ft_putstr(argv[i]);
		write(1, "\n", 1);
		i--;
	}
	return (0);
}
EX 03 ft_sort_params — CLÁSICO DE EXAMEN ★★★☆ Difícil

Imprime todos los argumentos (excepto argv[0]) ordenados según el orden ASCII, uno por línea.

→ Razonamiento: bubble sort sobre argv
  1. Necesitas comparar strings con tu ft_strcmp.
  2. Usa bubble sort: recorre el array, si argv[i] > argv[i+1] (en orden ASCII) los intercambias.
  3. Para comparar strings: ft_strcmp(argv[i], argv[i+1]) > 0 significa que argv[i] viene después en ASCII.
  4. El intercambio es de punteros (char *), no de strings completos. Solo intercambias las direcciones.
ft_sort_params.c
static int	ft_strcmp(char *s1, char *s2)
{
	int	i;

	i = 0;
	while (s1[i] == s2[i] && s1[i])
		i++;
	return ((unsigned char)s1[i] - (unsigned char)s2[i]);
}

int	main(int argc, char **argv)
{
	int		i;
	char	*tmp;

	i = 1;
	while (i < argc - 1)
	{
		if (ft_strcmp(argv[i], argv[i + 1]) > 0)
		{
			tmp = argv[i];       // intercambio de punteros
			argv[i] = argv[i + 1];
			argv[i + 1] = tmp;
			i = 1;              // reinicia para re-verificar
		}
		else
			i++;
	}
	i = 1;
	while (i < argc)
	{
		ft_putstr(argv[i]);
		write(1, "\n", 1);
		i++;
	}
	return (0);
}
💡 El intercambio es char *tmp = argv[i]; argv[i] = argv[i+1]; argv[i+1] = tmp; — intercambiamos punteros (direcciones), no copiamos strings. Más eficiente y más simple.
🚨 ¡Atención a la Norma! Tienes máx 5 variables en main. Aquí usas i y tmp, más argc y argv como parámetros. Los parámetros no cuentan como variables declaradas.
🎯 Recordatorio final — Examen sin internet
1. Header 42 en cada archivo (F1 en vim)
2. cc -Wall -Wextra -Werror — cero warnings
3. Prohibido: for · switch · goto · ternario · do...while
4. En C06: los programas llevan int main(int argc, char **argv)
5. Usa (void)argc si no usas el parámetro, para evitar warnings
6. El intercambio en ft_sort_params es de punteros, no de strings