42 Piscine · Guía de Estudio · Última Semana

C Piscine
Guía Completa

C00 · C01 · C02 · C03 — Ejercicios paso a paso, conceptos clave y la Norma

📅 Plan Semanal ⚠️ La Norma C00 · write & loops C01 · punteros C02 · strings C03 · comparación
Última Semana

Plan de Estudio — 7 días

💡 El examen final no tiene internet. Entrenas ahora para escribir de memoria. El objetivo esta semana es velocidad + precisión sin mirar la solución.
Día 1
Repaso C00
✓ ft_putchar
✓ ft_print_alphabet
✓ ft_print_reverse_alphabet
✓ ft_print_numbers
✓ ft_is_negative
→ ft_print_comb (difícil)
Día 2
C00 avanzado + Norma
→ ft_print_comb2
→ ft_putnbr
→ ft_print_combn (recursión)
✓ Leer norma completa
Día 3
Punteros — C01
✓ ft_ft
✓ ft_swap
→ ft_div_mod
→ ft_strlen
→ ft_putstr
Día 4
Strings — C02
✓ ft_strcpy
→ ft_strncpy
→ ft_str_is_alpha/numeric
→ ft_strupcase / strlowcase
→ ft_strcapitalize
Día 5
Comparación — C03
→ ft_strcmp
→ ft_strncmp
→ ft_strcat / ft_strncat
→ ft_strstr
Día 6
Simulacro de examen
🎯 Cerrar documentación
🎯 Elegir 3 ejs al azar
🎯 Resolver en 30min cada uno
🎯 Compilar con -Wall -Wextra -Werror
Día 7
Repaso final
🎯 Los que fallaste ayer
🎯 ft_putnbr de memoria
🎯 ft_print_comb de memoria
✓ Descanso — tú puedes
Norma v4

La Norma — Reglas Críticas

Violar la Norma = 0 automático aunque el código funcione perfectamente. La Moulinette y tus evaluadores lo comprobarán siempre.

Funciones

  • Máximo 25 líneas por función
  • Máximo 4 parámetros
  • Máximo 5 variables por función
  • Sin argumentos → prototipo con void
  • El return va entre paréntesis: return (val);
  • Máximo 5 funciones por archivo .c

Cosas Prohibidas

  • for → usar while
  • do...while
  • switch / case
  • goto
  • Operador ternario ?
  • VLAs (arrays de longitud variable)
  • Asignaciones múltiples: a = b = 0

Formato

  • Indentación con tabulaciones (no espacios)
  • Líneas máximo 80 columnas
  • Una instrucción por línea
  • Asterisco pegado al nombre: char *str
  • Una declaración de variable por línea
  • Declaraciones al inicio de la función
  • Línea vacía entre declaraciones y código

Header 42 obligatorio

  • Todos los .c y .h deben empezar con el header estándar de 42
  • En vim: :Stdheader o F1
  • En emacs: C-c C-h
  • Ejecutar norminette con: -R CheckForbiddenSourceHeader

Denominación

  • Structs empiezan con s_
  • Typedefs empiezan con t_
  • Unions empiezan con u_
  • Enums empiezan con e_
  • Macros y defines en MAYÚSCULAS

Compilación

  • Siempre: cc -Wall -Wextra -Werror
  • 0 warnings permitidos
  • Sin segfault, bus error, double free
  • Comportamiento sin definir = puede ser evaluado como 0
ejemplo_norma_correcto.c
/* Header 42 aquí arriba (obligatorio) */

int	ft_strlen(char *str)
{
	int	i;

	i = 0;
	while (str[i])
		i++;
	return (i);
}
⚠️ Nota el tab entre el tipo de retorno y el nombre de función (int⇥ft_strlen), el asterisco pegado al nombre (char *str), y el return con paréntesis.
C 00

C 00 — write, loops, ASCII

Concepto clave: En C, no tienes printf — usas write(1, &c, 1) para imprimir un carácter. Todo lo demás se construye a partir de ahí. Los caracteres son números: 'a' = 97, '0' = 48, 'z' = 122.
EX 00 ft_putchar ☆☆☆ Básico
void ft_putchar(char c);

Muestra el carácter c usando write. Es la función base de toda la Piscine.

→ Pasos
  1. La función recibe un char c como parámetro.
  2. Llama a write(1, &c, 1) — primer parámetro = stdout (1), segundo = dirección del char, tercero = 1 byte.
  3. No devuelve nada (void).
ft_putchar.c
#include <unistd.h>

void	ft_putchar(char c)
{
	write(1, &c, 1);
}
💡 &c es la dirección de memoria de c. write necesita un puntero, no el valor directamente.
EX 01 ft_print_alphabet ☆☆☆ Básico
void ft_print_alphabet(void);

Imprime el alfabeto en minúsculas de 'a' a 'z' en una sola línea.

→ Pasos
  1. Declara una variable char c e inicialízala a 'a' (= 97 en ASCII).
  2. Con un while, iterar mientras c <= 'z'.
  3. Dentro del loop: llama a ft_putchar(c) y luego c++.
ft_print_alphabet.c
void	ft_print_alphabet(void)
{
	char	c;

	c = 'a';
	while (c <= 'z')
	{
		ft_putchar(c);
		c++;
	}
}
EX 02 ft_print_reverse_alphabet ☆☆☆ Básico
void ft_print_reverse_alphabet(void);

Imprime el alfabeto en minúsculas de 'z' a 'a'.

→ Pasos
  1. Declara char c = 'z'.
  2. While loop mientras c >= 'a'.
  3. Imprime c, luego c--.
ft_print_reverse_alphabet.c
void	ft_print_reverse_alphabet(void)
{
	char	c;

	c = 'z';
	while (c >= 'a')
	{
		ft_putchar(c);
		c--;
	}
}
EX 03 ft_print_numbers ☆☆☆ Básico
void ft_print_numbers(void);

Imprime todos los dígitos del 0 al 9 en una sola línea.

ft_print_numbers.c
void	ft_print_numbers(void)
{
	char	c;

	c = '0';
	while (c <= '9')
	{
		ft_putchar(c);
		c++;
	}
}
⚠️ El 0 aquí es el carácter '0' (ASCII 48), no el entero 0. Siempre usa comillas simples para chars.
EX 04 ft_is_negative ★☆☆ Medio
void ft_is_negative(int n);

Muestra 'N' si n es negativo, 'P' si es positivo o cero.

🚨 No puedes usar el operador ternario (?) — está prohibido por la Norma. Usa if / else.
ft_is_negative.c
void	ft_is_negative(int n)
{
	if (n < 0)
		ft_putchar('N');
	else
		ft_putchar('P');
}
EX 05 ft_print_comb ☆ Difícil — suele caer en examen
void ft_print_comb(void);

Imprime todas las combinaciones de 3 dígitos distintos en orden creciente, separadas por , . Resultado esperado: 012, 013, ..., 789

→ Razonamiento (3 bucles anidados)
  1. Necesitas 3 contadores: a, b, c (chars de '0' a '9').
  2. a va de '0' a '7' (si a='8', no hay espacio para b y c distintos y mayores).
  3. b va de a+1 a '8'.
  4. c va de b+1 a '9'.
  5. Imprime a, b, c. Si no es la última combinación (789), imprime , después.
ft_print_comb.c
void	ft_print_comb(void)
{
	char	a;
	char	b;
	char	c;

	a = '0';
	while (a <= '7')
	{
		b = a + 1;
		while (b <= '8')
		{
			c = b + 1;
			while (c <= '9')
			{
				ft_putchar(a);
				ft_putchar(b);
				ft_putchar(c);
				if (!(a == '7' && b == '8' && c == '9'))
				{
					ft_putchar(',');
					ft_putchar(' ');
				}
				c++;
			}
			b++;
		}
		a++;
	}
}
💡 La condición !(a == '7' && b == '8' && c == '9') evita imprimir la coma después del último elemento (789).
EX 07 ft_putnbr ☆ Difícil — clásico de examen
void ft_putnbr(int nb);

Muestra el entero nb. Debe funcionar para todos los valores de int, incluyendo negativos y INT_MIN (-2147483648).

→ Razonamiento
  1. Caso especial: si nb == -2147483648, imprímelo directamente (no cabe en int positivo).
  2. Si nb es negativo, imprime '-' y niega nb.
  3. Si nb tiene más de un dígito (nb >= 10), llama a ft_putnbr recursivamente con nb/10.
  4. Imprime el último dígito: ft_putchar('0' + nb % 10).
ft_putnbr.c
void	ft_putnbr(int nb)
{
	if (nb == -2147483648)
	{
		ft_putchar('-');
		ft_putchar('2');
		ft_putnbr(147483648);
		return ;
	}
	if (nb < 0)
	{
		ft_putchar('-');
		nb = -nb;
	}
	if (nb >= 10)
		ft_putnbr(nb / 10);
	ft_putchar('0' + nb % 10);
}
🚨 INT_MIN = -2147483648. Si haces nb = -nb directamente, hay overflow porque 2147483648 no cabe en un int positivo (máximo 2147483647).
C 01

C 01 — Punteros

Concepto clave: Un puntero es una variable que guarda una dirección de memoria. Con *ptr accedes al valor que hay en esa dirección. Con &var obtienes la dirección de una variable. Modificar *ptr dentro de una función afecta al valor original.
punteros_concepto.c
int	x = 10;
int	*p = &x;  // p guarda la dirección de x

*p = 42;    // Modifica el valor en esa dirección
// Ahora x == 42
EX 00 ft_ft ☆☆☆ Básico
void ft_ft(int *nbr);

Recibe un puntero a int y le asigna el valor 42.

ft_ft.c
void	ft_ft(int *nbr)
{
	*nbr = 42;
}
💡 *nbr = 42 desreferencia el puntero y modifica el valor almacenado en esa dirección. No modifica el puntero en sí.
EX 02 ft_swap ★☆☆ Medio — clásico de examen
void ft_swap(int *a, int *b);

Intercambia el contenido de dos enteros a través de sus punteros.

→ Pasos
  1. Necesitas una variable temporal tmp para guardar uno de los valores.
  2. Guarda el valor de a en tmp: tmp = *a.
  3. Asigna el valor de b a a: *a = *b.
  4. Asigna tmp a b: *b = tmp.
ft_swap.c
void	ft_swap(int *a, int *b)
{
	int	tmp;

	tmp = *a;
	*a = *b;
	*b = tmp;
}
EX 03 ft_div_mod ★☆☆ Medio
void ft_div_mod(int a, int b, int *div, int *mod);

Divide a entre b. Guarda el cociente en *div y el resto en *mod.

ft_div_mod.c
void	ft_div_mod(int a, int b, int *div, int *mod)
{
	*div = a / b;
	*mod = a % b;
}
EX 05 ft_putstr ★☆☆ Medio — muy frecuente en examen
void ft_putstr(char *str);

Muestra los caracteres de un string uno a uno. Un string en C es un array de chars terminado en '\0' (null terminator).

→ Pasos
  1. Un string en C termina en el carácter '\0' (valor 0).
  2. Itera con while mientras *str != '\0' (o simplemente *str porque '\0' es falso).
  3. En cada iteración imprime *str y avanza el puntero con str++.
ft_putstr.c
void	ft_putstr(char *str)
{
	while (*str)
	{
		ft_putchar(*str);
		str++;
	}
}
EX 06 ft_strlen ★☆☆ Básico-Medio
int ft_strlen(char *str);

Cuenta y devuelve el número de caracteres del string (sin contar el '\0').

ft_strlen.c
int	ft_strlen(char *str)
{
	int	i;

	i = 0;
	while (str[i])
		i++;
	return (i);
}
C 02

C 02 — Strings y manipulación de caracteres

Concepto clave: Un string en C es char * — un puntero al primer carácter de una secuencia terminada en '\0'. Puedes acceder a cada char con str[i] o con *(str + i). Los rangos ASCII útiles: 'A'=65…'Z'=90, 'a'=97…'z'=122, '0'=48…'9'=57.
EX 00 ft_strcpy ★☆☆ Básico-Medio
char *ft_strcpy(char *dest, char *src);

Copia el string src en dest (incluyendo el '\0'). Devuelve dest.

→ Pasos
  1. Itera con un índice i mientras src[i] != '\0'.
  2. Copia cada carácter: dest[i] = src[i].
  3. Después del loop, añade el terminador: dest[i] = '\0'.
  4. Devuelve dest.
ft_strcpy.c
char	*ft_strcpy(char *dest, char *src)
{
	int	i;

	i = 0;
	while (src[i])
	{
		dest[i] = src[i];
		i++;
	}
	dest[i] = '\0';
	return (dest);
}
EX 02 ft_str_is_alpha ★☆☆ Medio
int ft_str_is_alpha(char *str);

Devuelve 1 si el string contiene solo letras (a-z, A-Z). Devuelve 0 si hay otro tipo. String vacío devuelve 1.

→ Lógica de validación
  1. Si *str == '\0' (string vacío), devuelve 1 directamente.
  2. Itera por cada carácter.
  3. Si el carácter no está entre 'a'-'z' NI entre 'A'-'Z', devuelve 0.
  4. Si termina el loop sin fallar, devuelve 1.
ft_str_is_alpha.c
int	ft_str_is_alpha(char *str)
{
	int	i;

	i = 0;
	while (str[i])
	{
		if (!((str[i] >= 'a' && str[i] <= 'z')
			|| (str[i] >= 'A' && str[i] <= 'Z')))
			return (0);
		i++;
	}
	return (1);
}
💡 El mismo patrón sirve para ft_str_is_numeric (solo '0'-'9'), ft_str_is_lowercase (solo 'a'-'z'), ft_str_is_uppercase (solo 'A'-'Z'), y ft_str_is_printable (ASCII 32-126).
EX 07 ft_strupcase ★☆☆ Medio
char *ft_strupcase(char *str);

Pone cada letra en mayúscula. Devuelve str.

→ Truco ASCII
  1. La diferencia entre 'a' y 'A' es exactamente 32 en ASCII.
  2. Para convertir minúscula → mayúscula: str[i] -= 32 (o str[i] = str[i] - 32).
  3. Solo aplica si el carácter está entre 'a' y 'z'.
ft_strupcase.c
char	*ft_strupcase(char *str)
{
	int	i;

	i = 0;
	while (str[i])
	{
		if (str[i] >= 'a' && str[i] <= 'z')
			str[i] -= 32;
		i++;
	}
	return (str);
}
EX 09 ft_strcapitalize ☆ Difícil
char *ft_strcapitalize(char *str);

Capitaliza la primera letra de cada "palabra" (secuencia alfanumérica), el resto en minúscula. Ejemplo: salut, comment tu vas ? 42motsSalut, Comment Tu Vas ? 42mots

→ Razonamiento
  1. Necesitas saber si estás al inicio de una palabra. Usa una variable new_word (1 = inicio de palabra, 0 = dentro de palabra).
  2. Empieza con new_word = 1 (el primer carácter siempre es inicio).
  3. Si el char actual es alfanumérico (letra o dígito): si new_word == 1, pónlo en mayúscula; si no, en minúscula. Luego new_word = 0.
  4. Si el char no es alfanumérico (espacio, coma, etc.): new_word = 1.
ft_strcapitalize.c
char	*ft_strcapitalize(char *str)
{
	int	i;
	int	new_word;

	i = 0;
	new_word = 1;
	while (str[i])
	{
		if ((str[i] >= 'a' && str[i] <= 'z')
			|| (str[i] >= 'A' && str[i] <= 'Z')
			|| (str[i] >= '0' && str[i] <= '9'))
		{
			if (new_word == 1 && str[i] >= 'a' && str[i] <= 'z')
				str[i] -= 32;
			else if (new_word == 0 && str[i] >= 'A' && str[i] <= 'Z')
				str[i] += 32;
			new_word = 0;
		}
		else
			new_word = 1;
		i++;
	}
	return (str);
}
C 03

C 03 — Comparación y concatenación de strings

Concepto clave: Comparar strings en C no es s1 == s2 — eso compara punteros. Hay que comparar carácter a carácter. La diferencia entre dos chars es lo que devuelven strcmp y sus variantes: negativo si s1 < s2, 0 si son iguales, positivo si s1 > s2.
EX 00 ft_strcmp ☆☆ Medio
int ft_strcmp(char *s1, char *s2);

Compara dos strings. Devuelve 0 si son iguales, negativo si s1 < s2, positivo si s1 > s2.

→ Razonamiento
  1. Itera mientras s1[i] == s2[i] Y el carácter no sea '\0'.
  2. Cuando el loop termina, devuelve la diferencia: (unsigned char)s1[i] - (unsigned char)s2[i].
  3. Si llegamos al final iguales, s1[i] = s2[i] = '\0', así que la diferencia es 0.
ft_strcmp.c
int	ft_strcmp(char *s1, char *s2)
{
	int	i;

	i = 0;
	while (s1[i] == s2[i] && s1[i] != '\0')
		i++;
	return ((unsigned char)s1[i] - (unsigned char)s2[i]);
}
💡 Se usa unsigned char para evitar problemas con caracteres con valores > 127 (extendidos). Es el comportamiento estándar de la libc.
EX 02 ft_strcat ★☆☆ Medio
char *ft_strcat(char *dest, char *src);

Concatena src al final de dest. Devuelve dest.

→ Pasos
  1. Primero, busca el final de dest: avanza un índice hasta llegar al '\0' de dest.
  2. Desde ese punto, copia todos los caracteres de src.
  3. Añade '\0' al final.
  4. Devuelve dest.
ft_strcat.c
char	*ft_strcat(char *dest, char *src)
{
	int	i;
	int	j;

	i = 0;
	j = 0;
	while (dest[i])
		i++;
	while (src[j])
	{
		dest[i] = src[j];
		i++;
		j++;
	}
	dest[i] = '\0';
	return (dest);
}
EX 04 ft_strstr ☆ Difícil
char *ft_strstr(char *str, char *to_find);

Busca la primera aparición de to_find dentro de str. Devuelve el puntero a esa posición, o NULL si no encuentra. Si to_find es vacío, devuelve str.

→ Razonamiento
  1. Si to_find[0] == '\0' (string vacío), devuelve str.
  2. Para cada posición i de str, comprueba si to_find encaja ahí.
  3. Compara carácter a carácter con un índice j.
  4. Si llegas al final de to_find sin fallar (to_find[j] == '\0'), devuelve str + i.
  5. Si no encontraste nada, devuelve NULL (0).
ft_strstr.c
char	*ft_strstr(char *str, char *to_find)
{
	int	i;
	int	j;

	if (!to_find[0])
		return (str);
	i = 0;
	while (str[i])
	{
		j = 0;
		while (str[i + j] == to_find[j] && to_find[j])
			j++;
		if (!to_find[j])
			return (str + i);
		i++;
	}
	return (0);
}
🎯
Recuerda para el examen
1. Escribe el header 42 en cada archivo antes de empezar.
2. Compila siempre con cc -Wall -Wextra -Werror — 0 warnings.
3. No uses for, switch, ternario (?), ni goto.
4. Declara variables al inicio de la función, con línea vacía antes del código.
5. return (valor); — siempre con paréntesis.
6. Máximo 25 líneas por función.