En esta página
Ejercicio 07: ft_putnbr
Muestra el número recibido como parámetro. La función debe ser capaz de mostrar cualquier valor posible de una variable de tipo
int.
| Dato | Valor |
|---|---|
| Carpeta | ex07/ |
| Archivo | ft_putnbr.c |
| Prototipo | void ft_putnbr(int nb); |
| Funciones autorizadas | solo write |
Hecho con amor para Laura, por Bogdan.
El archivo
#include <unistd.h>
/*
** Copia local de ft_putchar: el enunciado solo permite entregar este archivo.
** Escribe un unico caracter en la salida estandar.
*/
static void put_char(char c)
{
write(1, &c, 1);
}
/*
** Imprime un entero sin signo digito a digito.
** Es recursiva para que el digito mas significativo salga primero: primero
** se resuelve el numero dividido entre 10 y despues se escribe el resto.
*/
static void put_unsigned(unsigned int number)
{
if (number >= 10)
put_unsigned(number / 10);
put_char('0' + number % 10);
}
/*
** Muestra cualquier valor posible de un int.
** El negativo se convierte a unsigned antes de cambiarle el signo: asi
** INT_MIN, que no cabe en un int positivo, tampoco desborda.
*/
void ft_putnbr(int nb)
{
if (nb < 0)
{
put_char('-');
put_unsigned(-(unsigned int)nb);
}
else
put_unsigned((unsigned int)nb);
}
Línea por línea
#include <unistd.h>
Declara write. Sin esta línea, -Werror detiene la compilación.
static void put_char(char c)
Copia privada de ft_putchar. Escribe un byte en la salida estándar.
static void put_unsigned(unsigned int number)
Imprime un entero sin signo dígito a dígito. Es la parte que de verdad
trabaja; ft_putnbr solo se ocupa del signo.
unsigned int es un entero que no admite negativos y puede llegar hasta
4294967295. Se usa aquí para el caso extremo que se explica más abajo.
if (number >= 10)
El caso recursivo. Si el número tiene más de una cifra, primero hay que imprimir
las anteriores. La llamada es a sí misma con number / 10, que quita la última
cifra:
| Llamada | number / 10 |
|---|---|
put_unsigned(42) | 4 |
put_unsigned(4) | 0, no entra en el if |
put_char('0' + number % 10);
Imprime la última cifra. Está después del if, así que se ejecuta al volver
de la recursión. Ese orden es toda la gracia: la cifra más significativa sale
primero porque su llamada es la que termina antes de imprimir.
Traza de put_unsigned(42):
| Paso | Qué ocurre | Qué se escribe |
|---|---|---|
| 1 | 42 >= 10, llama a put_unsigned(4) | — |
| 2 | 4 >= 10 es falso, escribe 4 % 10 | 4 |
| 3 | vuelve a la llamada de 42 y escribe 42 % 10 | 2 |
Resultado: 42. Con un while normal saldría 24.
void ft_putnbr(int nb)
La función pública, con el prototipo exacto del enunciado.
if (nb < 0)
Los negativos necesitan dos cosas: el signo menos y el número en positivo.
put_char('-');
El signo se imprime primero, antes de los dígitos.
put_unsigned(-(unsigned int)nb);
Aquí está el detalle fino del ejercicio. La conversión a unsigned ocurre
antes del cambio de signo, y eso no es casualidad.
else y put_unsigned((unsigned int)nb);
Para cero y positivos no hay signo que escribir: se pasa el número tal cual. La
conversión a unsigned no cambia el valor en este rango.
El problema de INT_MIN
El mínimo de un int es -2147483648. Su positivo, 2147483648, no cabe en
un int, cuyo máximo es 2147483647. Escribir -nb con ese valor es
comportamiento indefinido: el estándar no dice qué pasa, y con optimizaciones
puede dar cualquier cosa.
La solución del código es pasar a unsigned antes de negar:
-(unsigned int)nb
Convertir un negativo a unsigned está definido: se le suma 2³². Así
(unsigned int)(-2147483648) vale 2147483648, que sí cabe en un unsigned, y
negarlo devuelve ese mismo número. Ningún paso desborda.
Valor de nb | (unsigned int)nb | Resultado |
|---|---|---|
42 | 42 | 42 |
-42 | 4294967254 | -… = 42 |
-2147483648 | 2147483648 | 2147483648 |
Por qué no imprime salto de línea
El enunciado pide mostrar el número, no una línea. ft_putnbr(42) escribe
exactamente 42; el \n lo pone quien llama:
ft_putnbr(42);
write(1, "\n", 1);
Salida esperada
| Llamada | Salida |
|---|---|
ft_putnbr(0) | 0 |
ft_putnbr(42) | 42 |
ft_putnbr(-42) | -42 |
ft_putnbr(2147483647) | 2147483647 |
ft_putnbr(-2147483648) | -2147483648 |
Errores típicos
| Mensaje o síntoma | Causa | Solución |
|---|---|---|
| los dígitos salen al revés | usaste un while sin recursión | imprime después de la llamada |
INT_MIN imprime basura | hiciste -nb en un int | pasa a unsigned primero |
warning: comparison of unsigned… | comparaste un unsigned con 0 | no hace falta: unsigned nunca es negativo |
falta el - | no trataste los negativos | imprime '-' antes |
| se cuelga | la recursión no reduce el número | divide entre 10 |
Comprobarlo
cat > /tmp/main.c <<'EOF'
#include <unistd.h>
void ft_putnbr(int nb);
int main(void)
{
ft_putnbr(0);
write(1, "\n", 1);
ft_putnbr(42);
write(1, "\n", 1);
ft_putnbr(-42);
write(1, "\n", 1);
ft_putnbr(2147483647);
write(1, "\n", 1);
ft_putnbr(-2147483648);
write(1, "\n", 1);
return (0);
}
EOF
cc -Wall -Wextra -Werror ex07/ft_putnbr.c /tmp/main.c -o /tmp/ex07
/tmp/ex07
Salida:
0
42
-42
2147483647
-2147483648