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.

DatoValor
Carpetaex07/
Archivoft_putnbr.c
Prototipovoid ft_putnbr(int nb);
Funciones autorizadassolo 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:

Llamadanumber / 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):

PasoQué ocurreQué se escribe
142 >= 10, llama a put_unsigned(4)—
24 >= 10 es falso, escribe 4 % 104
3vuelve a la llamada de 42 y escribe 42 % 102

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)nbResultado
424242
-424294967254-… = 42
-214748364821474836482147483648

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

LlamadaSalida
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íntomaCausaSolución
los dígitos salen al revésusaste un while sin recursiónimprime después de la llamada
INT_MIN imprime basurahiciste -nb en un intpasa a unsigned primero
warning: comparison of unsigned…comparaste un unsigned con 0no hace falta: unsigned nunca es negativo
falta el -no trataste los negativosimprime '-' antes
se cuelgala recursión no reduce el númerodivide 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

Descargar