Función printf()

Sintaxis:

int printf(const char *formato[, argumento, ...]);

Escribe una cadena con formato a la salida estándar stdout.

Esta es probablemente una de las funciones más complejas de C. No es necesario que la estudies en profundidad, limítate a leer este capítulo, y considéralo como una fuente para la consulta. Descubrirás que poco a poco la conoces y dominas.

Esta función acepta listas de parámetros con un número indefinido de elementos. Entendamos que el número está indefinido en la declaración de la función, pero no en su uso, el número de argumentos dependerá de la cadena de formato, y conociendo ésta, podrá precisarse el número y tipo de los argumentos. Si no se respeta esta regla, el resultado es impredecible, y normalmente desastroso, sobre todo cuando faltan argumentos. En general, los sobrantes serán simplemente ignorados.

Cada argumento se aplicará a la cadena de formato y la cadena resultante se enviará a la pantalla.

Valor de retorno:

Si todo va bien el valor de retorno será el número de bytes de la cadena de salida. Si hay error se retornará con EOF.

Veremos ahora las cadenas de formato. Estas cadenas controlan cómo se tratará cada uno de los argumentos, realizando la conversión adecuada, dependiendo de su tipo. Cada cadena de formato tiene dos tipos de objetos:

  • Caracteres simples, que serán copiados literalmente a la salida.
  • Descriptores de conversión que tomarán los argumentos de la lista y les aplicarán el formato adecuado.

A su vez, los descriptores de formato tienen la siguiente estructura:

%[opciones][anchura][.precisión] [F|N|h|l|L] carácter_de_tipo 

Cada descriptor de formato empieza siempre con el carácter '%', después de él siguen algunos componentes opcionales y finalmente un carácter que indica el tipo de conversión a realizar.

Componente Opcional/Obligatorio Tipo de control
[opciones] Opcional Tipo de justificación, signos de números, puntos decimales, ceros iniciales, prefijos octales y hexadecimales.
[anchura] Opcional Anchura, mínimo número de caracteres a imprimir, se completa con espacios o ceros.
[precisión] Opcional Precisión, máximo número de caracteres. Para enteros, mínimo número de caracteres a imprimir.
[F|N|h|l|L] Opcional

Modificador de tamaño de la entrada. Ignora el tamaño por defecto para los parámetros de entrada.

F = punteros lejanos (far pointer)
N = punteros cercanos (near pointer)
h = short int 
l = long 
L = long double
Carácter_de_tipo Obligatorio Carácter de conversión de tipos.

Opciones

Pueden aparecer en cualquier orden y combinación:

Opción Significado
- Justifica el resultado a la izquierda, rellena a la derecha con espacios, si no se da se asume justificación a la derecha, se rellena con ceros o espacios a la izquierda.
+ El número se mostrará siempre con signo (+) o (-), según corresponda.
Espacio Igual que el anterior, salvo que cuando el signo sea positivo se mostrará un espacio.
# Especifica que el número se convertirá usando un formato alternativo

La opción (#) tiene diferentes efectos según el carácter de tipo especificado en el descriptor de formato, si es:

Carácter de tipo Efecto
c s d i u No tiene ningún efecto.
x X Se añadirá 0x (o 0X) al principio del argumento.
e E f g G En el resultado siempre mostrará el punto decimal, aunque ningún dígito le siga. Normalmente, el punto decimal sólo se muestra si le sigue algún dígito.

Anchura:

Define el número mínimo de caracteres que se usarán para mostrar el valor de salida.

Puede ser especificado directamente mediante un número decimal, o indirectamente mediante un asterisco (*). Si se usa un asterisco como descriptor de anchura, el siguiente argumento de la lista, que debe ser un entero, que especificará la anchura mínima de la salida. Aunque no se especifique o sea más pequeño de lo necesario, el resultado nunca se truncará, sino que se expandirá lo necesario para contener el valor de salida.

Descriptor Efecto
n Al menos n caracteres serán impresos, si la salida requiere menos de n caracteres se rellenará con blancos.
0n Lo mismo, pero se rellenará a la izquierda con ceros.
* El número se tomará de la lista de argumentos

Precisión:

Especifica el número máximo de caracteres o el mínimo de dígitos enteros a imprimir.

La especificación de precisión siempre empieza con un punto (.) para separarlo del descriptor de anchura.

Al igual que el descriptor de anchura, el de precisión admite la especificación directa, con un número; o indirecta, con un asterisco (*).

Si usas un asterisco como descriptor de anchura, el siguiente argumento de la lista, que debe ser un entero, especificará la anchura mínima de la salida. Si usas el asterisco para el descriptor de precisión, el de anchura, o para ambos, el orden será, descriptor de anchura, de precisión y el dato a convertir.

Descriptor Efecto
(nada) Precisión por defecto
.0 ó .

Para los tipos d, i, o, u, x, precisión por defecto.

Para e, E, f, no se imprimirá el punto decimal, ni ningún decimal.

.n Se imprimirán n caracteres o n decimales. Si el valor tiene más de n caracteres se truncará o se redondeará, según el caso.
.* El descriptor de precisión se tomará de la lista de argumentos.

Valores de precisión por defecto:

1 para los tipos: d,i,o,u,x,X

6 para los tipos: e,E,f

Todos los dígitos significativos para los tipos; g, G

Hasta el primer nulo para el tipo s

Sin efecto en el tipo c

Si se dan las siguientes condiciones no se imprimirán caracteres para este campo:

  • Se especifica explícitamente una precisión de 0.
  • El campo es un entero (d, i, o, u, óx),
  • El valor a imprimir es cero.

Cómo afecta [.precisión] a la conversión:

Carácter de tipo Efecto de (.n)
d i o u x X Al menos n dígitos serán impresos. Si el argumento de entrada tiene menos de n dígitos se rellenará a la izquierda, para x/X con ceros. Si el argumento de entrada tiene menos de n dígitos el valor no será truncado.
e E f Al menos n caracteres se imprimirán después del punto decimal, el último de ellos será redondeado.
g G Como máximo, n dígitos significativos serán impresos.
c Ningún efecto.
s No más de n caracteres serán impresos.

Modificador de tamaño [F|N|h|l|L]:

Indican cómo printf debe interpretar el siguiente argumento de entrada.

Modificador Tipo de argumento Interpretación
F Puntero p s n Un puntero far
N Puntero p s n Un puntero near
h d i o u x X short int
L d i o u x X long int
l e E f g G double
L e E f g G long double

Caracteres de conversión de tipo

La información de la siguiente tabla asume que no se han especificado opciones, ni descriptores de ancho ni precisión, ni modificadores de tamaño.

Carácter de tipo Entrada esperada Formato de salida
Números    
d Entero con signo Entero decimal
i Entero con signo Entero decimal
o Entero con signo Entero octal
u Entero sin signo Entero decimal
x Entero sin signo Entero hexadecimal (con a, b, c, d, e, f)
X Entero sin signo Entero hexadecimal (con A, B, C, D, E, F)
f Coma flotante Valor con signo: [-]dddd.dddd
e Coma flotante Valor con signo: [-]d.dddd...e[+/-]ddd
g Coma flotante Valor con signo, dependiendo del valor de la precisión. Se rellenará con ceros y se añadirá el punto decimal si es necesario.
E Coma flotante Valor con signo: [-]d.dddd...E[+/-]ddd
G Coma flotante Como en g, pero se usa E para los exponentes.
Caracteres    
c Carácter Un carácter.
s Puntero a cadena Caracteres hasta que se encuentre un nulo o se alcance la precisión especificada.
Especial    
% Nada El carácter '%'
Punteros    
n Puntero a int Almacena la cuenta de los caracteres escritos.
p Puntero Imprime el argumento de entrada en formato de puntero: XXXX:YYYY ó YYYY

Veamos algunos ejemplos que nos aclaren este galimatías, en los comentarios a la derecha de cada printf se muestra la salida prevista:

#include <cstdio> 
using namespace std;

int main() { 
   int i = 123; 
   int j = -124; 
   float x = 123.456; 
   float y = -321.12; 
   char Saludo[5] = "hola"; 

   printf("|%6d|\n", i); // | 123| 
   printf("|%-6d|\n", i); // |123 | 
   printf("|%06d|\n", i); // |000123| 
   printf("|%+6d|\n", i); // | +123| 
   printf("|%+6d|\n", j); // | -124| 
   printf("|%+06d|\n", i); // |+00123| 
   printf("|% 06d|\n", i); // | 00123| 
   printf("|%6o|\n", i); // | 173| 
   printf("|%#6o|\n", i); // | 0173| 
   printf("|%06o|\n", i); // |000173| 
   printf("|%-#6o|\n", i); // |0173 | 
   printf("|%6x|\n", i); // | 7b| 
   printf("|%#6X|\n", i); // | 0X7B| 
   printf("|%#06X|\n", i); // |0X007B| 
   printf("|%-#6x|\n", i); // |0x7b | 
   printf("|%10.2f|\n", x); // | 123.46| 
   printf("|%10.4f|\n", x); // |  123.4560| 
   printf("|%010.2f|\n", x); // |0000123.46| 
   printf("|%-10.2f|\n", x); // |123.46 | 
   printf("|%10.2e|\n", x); // | 1.23e+02| 
   printf("|%-+10.2e|\n", y);// |-3.21e+02 | 
   printf("|%*.*f|\n", 14, 4, x); // | 123.4560| 
   printf("%.2f  es el 10%% de %.2f\n", .10*x, x); // 12.35 es el 10% de 123.46 
   printf("%s es un saludo y %c una letra\n", Saludo, Saludo[2]); // hola es un saludo y l una letra 
   printf("%.2s es parte de un saludo\n", Saludo); // ho es parte de un saludo 
}

Observa el funcionamiento de este ejemplo, modifícalo y experimenta. Intenta predecir los resultados.