AnteriorPosterior

4. Entrada/salida básica

  Curso: Fundamentos de programación en C, por Nacho Cabanes

Vamos a ver con algo más de detalle las órdenes habituales de entrada y salida estándar: printf y scanf, que ya conocemos, putchar y getchar, que aun no habíamos visto. Todas ellas se encuentran definidas en <stdio.h>

4.1. printf

Ya conocemos el manejo básico de la orden “printf”:

printf( formato [, dato1, dato2, ...])

(el corchete indica que la cadena de texto de formato debe existir siempre, pero los datos adicionales son opcionales, pueden no aparecer).

Esta orden muestra un texto formateado en pantalla. Para usarla es necesario incluir <stdio.h> al principio de nuestro programa. Hemos visto cómo emplearla para mostrar número enteros, números reales y caracteres. Ahora vamos a ver más detalles sobre qué podemos mostrar y cómo hacerlo:

Los “especificadores de formato” que podemos usar son:

c

Un único carácter

d

Un número entero decimal (en base 10) con signo

f

Un número real (coma flotante)

e

Un número real en notación exponencial, usando la ?e? minúscula

E

Un número real en notación exponencial, usando la ?E? mayúscula

g

Usa ?e? o ?f? (el más corto), con ?e? minúscula

G

Usa ?e? o ?f? (el más corto), con ?E? mayúscula

i

Un número entero decimal con signo

u

Un número entero decimal sin signo (unsigned)

h

Corto (modificador para un entero)

l

Largo (modificador para un entero)

x

Un número entero decimal sin signo en hexadecimal (base 16)

X

Un número entero decimal sin signo en hexadecimal en mayúsculas

o

Un número entero decimal sin signo en octal (base 8)

s

Una cadena de texto (que veremos en el próximo tema)

 

Si usamos %% se mostrará el símbolo de porcentaje en pantalla.

Queda alguna otra posibilidad que todavía es demasiado avanzada para nosotros, y que comentaremos mucho más adelante, cuando hablemos de “punteros”.

Además, las órdenes de formato pueden tener modificadores, que se sitúan entre el % y la letra identificativa del código.

> Si el modificador es un número, especifica la anchura mínima en la que se escribe ese argumento (por ejemplo: %5d).
> Si ese número empieza por 0, los espacios sobrantes (si los hay) de la anchura mínima se rellenan con 0 (por ejemplo: %07d).
> Si ese número tiene decimales, indica el número de dígitos totales y decimales si los que se va a escribir es un número (por ejemplo %5.2f), o la anchura mínima y máxima si se trata de una cadena de caracteres (como %10.10s).
> Si el número es negativo, la salida se justificará a la izquierda, dejando espacios en blanco al final (en caso contrario, si no se dice nada, se justifica a la derecha, dejando los espacios al principio).

Vamos a ver un ejemplo de todo esto:

/*---------------------------*/
/*  Ejemplo en C nº 36:      */
/*  C036.C                   */
/*                           */
/*  Detalles de "printf"     */
/*                           */
/*  Curso de C,              */
/*    Nacho Cabanes          */
/*---------------------------*/
 
#include <stdio.h>
 
int main()
{
    int   entero = 1234;
    int   enteroNeg = -1234;
    float real = 234.567;
    char  letra = 'E';
    int   contador;
 
    printf("El número entero vale %d en notación decimal,\n", entero);
    printf("  y %o en notación octal,\n", entero);
    printf("  y %x en notación hexadecimal,\n", entero);
    printf("  y %X en notación hexadecimal en mayúsculas,\n", entero);
    printf("  y %ld si le hacemos que crea que es entero largo,\n", entero);
    printf("  y %10d si obligamos a una cierta anchura,\n", entero);
    printf("  y %-10d si ajustamos a la izquierda.\n", entero);
    printf("El entero negativo vale %d\n", enteroNeg);
    printf("  y podemos hacer que crea que es positivo: %u (incorrecto).\n",
        enteroNeg);
    printf("El número real vale %f en notación normal\n", real);
    printf("  y %5.2f si limitamos a dos decimales,\n", real);    
    printf("  y %e en notación científica (exponencial).\n", real);
    printf("La letra es %c y un texto es %s.\n", letra, "Hola");
    printf("  Podemos poner \"tanto por ciento\": 50%%.\n");
 
    return 0;
}
 

El resultado de este programa sería:

El número entero vale 1234 en notación decimal,
y 2322 en notación octal,
y 4d2 en notación hexadecimal,
y 4D2 en notación hexadecimal en mayúsculas,
y 1234 si le hacemos que crea que es entero largo,
y 1234 si obligamos a una cierta anchura,
y 1234 si ajustamos a la izquierda.
El entero negativo vale -1234
y podemos hacer que crea que es positivo: 4294966062 (incorrecto).
El número real vale 234.567001 en notación normal
y 234.57 si limitamos a dos decimales,
y 2.345670e+002 en notación científica (exponencial).
La letra es E y el texto Hola.
Podemos poner "tanto por ciento": 50%.

Casi todo es fácil de seguir, pero aún así vemos alguna cosa desconcertante...

Por ejemplo, ¿por qué el número real aparece como 234.567001, si nosotros lo hemos definido como 234.567? Hay que recordar que los números reales se almacenan con una cierta pérdida de precisión. En un “float” sólo se nos garantiza que unas 6 cifras sean correctas. Si mostramos el número con más de 6 cifras (estamos usando 9), puede que el resultado no sea totalmente correcto. Si esta pérdida de precisión es demasiado grande para el uso que queramos darle, deberemos usar otro tipo de datos, como double.

Lo de que el número negativo quede mal cuando lo intentamos escribir como positivo, también nos suena conocido: si el primer bit de un número con signo es uno, indica que es un número negativo, mientras que en un número positivo el primer bit es la cifra binaria más grande (lo que se conoce como “el bit más significativo”). Por eso, tanto el número -1234 como el 4294966062 se traducen en la misma secuencia de ceros y unos, que la sentencia “printf” interpreta de una forma u otra según le digamos que el número el positivo o negativo.

Como curiosidad, ese número 4294966062 sería un valor distinto (64302) si usáramos un compilador de 16 bits en vez de uno de 32, porque sería una secuencia de 16 ceros y unos, en vez de una secuencia de 32 ceros y unos.

Otra opción más avanzada es que si usamos un asterisco (*) para indicar la anchura o la precisión, los primeros datos de la lista serán los valores que se tomen para indicar la anchura y la precisión real que se usarán:

int minimo = 5;
int máximo = 10;
printf("%*.*s", minimo, maximo, "mensaje");

Ejercicios propuestos:

  • Un programa que pida al usuario un número entero y muestre sus equivalentes en formato hexadecimal y en octal. Deberá seguir pidiendo (y convirtiendo) números hasta que se introduzca 0.
  • Un programa que pida al usuario 2 números reales y muestre su división con 2 decimales (excepto si el segundo es 0; en ese caso, deberá decir ?no se puede dividir?).

 

4.2. scanf

Como ya sabemos, el uso de “scanf” recuerda mucho al de “printf”, salvo porque hay que añadir el símbolo & antes de los nombres de las variables en las que queremos almacenar los datos. Aun así, los códigos de formato no son exactamente los mismos. Vamos a ver los más importantes:

c

Un único carácter

d

Un número entero decimal (base 10) con signo

D

Un entero largo en base 10 sin signo

f

Un número real (coma flotante)

e,E

Un número real en notación exponencial

g,G

Permite ?e? o ?f?

i

Un número entero con signo

I

Un número entero largo con signo

u

Un número entero decimal sin signo (unsigned)

U

Un número entero decimal largo sin signo (unsigned)

h

Corto (modificador, para un entero)

l

Largo (modificador, para un entero)

x

Un número entero sin signo en hexadecimal (base 16)

X

Un número entero largo sin signo en hexadecimal

o

Un número entero sin signo en octal (base 8)

O

Un número entero largo sin signo en octal (base 8)

s

Una cadena de texto (que veremos en el próximo tema)

 

Como vemos, la diferencia más importante es que si en vez de un entero queremos un entero largo, se suele usar el mismo carácter escrito en mayúsculas.

Al igual que en “printf”, se puede indicar un número antes del identificador, que nos serviría para indicar la cantidad máxima de caracteres a leer. Por ejemplo, “scanf("%10s", &texto)” nos permitiría leer un texto de un tamaño máximo de 10 letras.

Aun así, “scanf” a veces da resultados desconcertantes, por lo que no es la orden más adecuada cuando se pretende crear un entorno amigable. Más adelante veremos formas alternativas de leer del teclado.

Ejercicios propuestos:

  • Un programa que pida al usuario un número hexadecimal y muestre su equivalente en base 10.
  • Un programa que pida al usuario un número octal y muestre su equivalente en base 10.
  • Un programa que pida al usuario una letra, después le pida una segunda letra, y las muestre en el orden contrario al que se introdujeron.

4.3. putchar

Es una forma sencilla de escribir un único carácter en la pantalla:

putchar('A');

o si usamos una variable:

putchar(x);

Ejercicios propuestos:

•  Un programa que escriba las letras de la A (a mayúscula) a la Z (z mayúscula), usando ?for? y ?putchar?.

4.4. getchar

Lo habíamos usado desde un principio en algunos entornos de programación para Windows, como forma de detener momentáneamente la ejecución. Realmente es más que eso: lee el siguiente carácter que esté disponible en el buffer del teclado (una memoria intermedia que almacena todas las pulsaciones de teclas que hagamos):

letra = getchar();

Si no quedaran más letras por leer, el valor que nos daría es EOF, una constante predefinida que nos indicará el final de un fichero (End Of File) o, en este caso, de la entrada disponible por teclado. Se usaría así:

letra = getchar();
if (letra==EOF) printf("No hay más letras");

 

Vamos a ver un ejemplo del uso de getchar y de putchar:

/*---------------------------*/
/*  Ejemplo en C nº 37:      */
/*  C037.C                   */
/*                           */
/*  getchar y putchar        */
/*                           */
/*  Curso de C,              */
/*    Nacho Cabanes          */
/*---------------------------*/
 
#include <stdio.h>
 
int main(#41;
{
    char letra1, letra2;
 
    printf("Introduce dos letras y pulsa Intro: ");
    letra1 = getchar();
    letra2 = getchar();
    printf("Has tecleado: ");
    putchar(letra1);
    printf(" y también %c", letra2);
 
    return 0;
}
 

Vemos que aunque “getchar” lea tecla a tecla, no se analiza lo que hemos tecleado hasta que se pulsa Intro. Si tecleamos varias letras, la primera vez que usemos getchar nos dirá cual era la primera, la siguiente vez que usemos getchar nos dirá la segunda letra y así sucesivamente.

En este ejemplo sólo leemos dos letras. Si se teclean tres o más, las últimas se pierden. Si se teclea una letra y se pulsa Intro, “letra1” será la letra tecleada... ¡y “letra2” será el Intro (el carácter ‘\n’ de avance de línea)!

Estos problemas también los tenemos si intentamos leer letra a letra con “scanf("%c", ...” así que para hacer esto de forma fiable necesitaremos otras órdenes, que no son estándar en C sino que dependerán de nuestro compilador y nuestro sistema operativo, y que veremos más adelante.

Una alternativa sería una segunda orden ?getchar? para absorber la pulsación de la tecla Intro:

tecla = getchar(); getchar();

 

O bien leer dos caracteres con ?scanf? (la letra esperada y el Intro que queremos absorber):

scanf("%c%c", &tecla);

 

Ejercicios propuestos:

•  Un programa que pida al usuario 4 letras (usando ?getchar?) y las muestre en orden inverso (por ejemplo, si las letras son ?h o l a?, escribiría ?aloh?).

Actualizado el: 24-07-2014 15:27

AnteriorPosterior