La familia de funciones printf en el lenguaje de programación C es un conjunto de funciones que toman una cadena de formato como entrada entre una lista de tamaño variable de otros valores y producen como salida una cadena que corresponde al especificador de formato y a los valores de entrada dados. La cadena está escrita en un lenguaje de plantilla simple : los caracteres generalmente se copian literalmente en la salida de la función, pero los especificadores de formato , que comienzan con un %carácter, indican la ubicación y el método para traducir un dato (como un número) a caracteres. El diseño se ha copiado para exponer una funcionalidad similar en otros lenguajes de programación .
"printf" es el nombre de una de las principales funciones de salida de C y significa " imprimir formateado". Las cadenas en formato printf son complementarias a las cadenas en formato scanf , que proporcionan entrada formateada ( lexing también conocido como parsing ). En ambos casos, estos proporcionan una funcionalidad simple y un formato fijo en comparación con motores de plantillas o lexers/analizadores más sofisticados y flexibles, pero son suficientes para muchos propósitos.
Muchos lenguajes distintos de C copian la sintaxis de cadena en formato printf de manera cercana o exacta en sus propias funciones de E/S.
Las discrepancias entre los especificadores de formato y el tipo de datos pueden provocar fallos y otras vulnerabilidades. La cadena de formato en sí suele ser una cadena literal , lo que permite el análisis estático de la llamada a la función. Sin embargo, también puede ser el valor de una variable, lo que permite el formateo dinámico pero también una vulnerabilidad de seguridad conocida como exploit de cadena de formato no controlado .
Los primeros lenguajes de programación, como Fortran, utilizaban declaraciones especiales con una sintaxis completamente diferente a la de otros cálculos para crear descripciones de formato. En este ejemplo, el formato se especifica en la línea 601 y el comando WRITE hace referencia a él por número de línea:
ESCRIBIR CINTA DE SALIDA 6 , 601 , IA , IB , IC , ÁREA 601 FORMATO ( 4 H A = , I5 , 5 H B = , I5 , 5 H C = , I5 , & 8 H ÁREA = , F10 . 2 , 13 H UNIDADES CUADRADA )
ALGOL 68 tenía una API más similar a funciones , pero aún usaba una sintaxis especial (los $
delimitadores rodean la sintaxis de formato especial):
printf (( $ "Color " g ", numero1 " 6 d , ", numero2 " 4 zd , ", hexadecimal " 16 r2d , ", float " - d .2 d , ", valor sin signo" -3 d "." l$ , "rojo" , 123456 , 89 , BIN 255 , 3,14 , 250 ));
Pero el uso de llamadas a funciones y tipos de datos normales simplifica el lenguaje y el compilador, y permite que la implementación de la entrada/salida se escriba en el mismo lenguaje. Estas ventajas superan las desventajas (como la falta total de seguridad de tipos en muchos casos) y en la mayoría de los lenguajes más nuevos, la E/S no forma parte de la sintaxis.
C printf
tiene su origen en la función BCPLwritef
(1966). En comparación con C
y printf
, es una secuencia de escape del lenguaje*N
BCPL que representa un carácter de nueva línea (para el cual C usa la secuencia de escape ) y el orden del ancho y tipo de campo de la especificación de formato se invierte en : [1]\n
writef
WRITEF("EL PROBLEMA DE %I2-QUEENS TIENE %I5 SOLUCIONES*N", NUMQUEENS, COUNT)
Probablemente la primera copia de la sintaxis fuera del lenguaje C fue el printf
comando shell de Unix, que apareció por primera vez en la Versión 4 , como parte de la adaptación a C. [2]
El formateo se realiza mediante marcadores de posición dentro de la cadena de formato. Por ejemplo, si un programa quisiera imprimir la edad de una persona, podría presentar el resultado anteponiendo "Tu edad es " y usando el carácter especificador decimal con signo d
para indicar que queremos que el número entero de la edad se muestre inmediatamente. después de ese mensaje, podemos usar la cadena de formato:
printf ( "Tu edad es %d" , edad );
La sintaxis de un marcador de posición de formato es
%[ parámetro ][ banderas ][ ancho ][. precisión ][ longitud ] tipo
Esta es una extensión POSIX y no en C99 . El campo Parámetro se puede omitir o puede ser:
Esta característica se utiliza principalmente en la localización, donde el orden de aparición de los parámetros varía debido a la convención que depende del idioma.
En Microsoft Windows que no es POSIX, la compatibilidad con esta característica se coloca en una función printf_p separada.
El campo Banderas puede ser cero o más (en cualquier orden) de:
El campo Ancho especifica una cantidad mínima de caracteres para generar y generalmente se usa para rellenar campos de ancho fijo en la salida tabulada, donde de otro modo los campos serían más pequeños, aunque no provoca el truncamiento de campos de gran tamaño.
Se puede omitir el campo ancho, o un valor entero numérico, o un valor dinámico cuando se pasa como otro argumento cuando se indica con un asterisco *
. [3] Por ejemplo, printf("%*d", 5, 10)
se imprimirá 10
con un ancho total de 5 caracteres.
Aunque no forma parte del campo de ancho, un cero a la izquierda se interpreta como el indicador de relleno con ceros mencionado anteriormente, y un valor negativo se trata como el valor positivo junto con el -
indicador de alineación a la izquierda también mencionado anteriormente.
El campo Precisión normalmente especifica un límite máximo en la salida, dependiendo del tipo de formato particular. Para tipos numéricos de punto flotante, especifica el número de dígitos a la derecha del punto decimal que se debe redondear la salida. Para el tipo de cadena, limita la cantidad de caracteres que deben generarse, después de lo cual la cadena se trunca.
Se puede omitir el campo de precisión, o un valor entero numérico, o un valor dinámico cuando se pasa como otro argumento cuando se indica con un asterisco *
. Por ejemplo, printf("%.*s", 3, "abcdef")
resultará en abc
ser impreso.
El campo Longitud se puede omitir o ser cualquiera de:
Además, surgieron varias opciones de longitud específicas de la plataforma antes del uso generalizado de las extensiones ISO C99:
ISO C99 incluye el inttypes.h
archivo de encabezado que incluye una serie de macros para usar en printf
codificación independiente de la plataforma. Deben estar fuera de comillas dobles, por ejemploprintf("%" PRId64 "\n", t);
Las macros de ejemplo incluyen:
El campo Tipo puede ser cualquiera de:
Hay algunas implementaciones de printf
funciones similares que permiten extensiones al minilenguaje basado en caracteres de escape , lo que permite al programador tener una función de formato específica para tipos no integrados. Uno de los más conocidos es el (ahora obsoleto) Register_printf_function() de glibc . Sin embargo, rara vez se utiliza debido al hecho de que entra en conflicto con la verificación de cadenas de formato estático. Otro son los formateadores personalizados Vstr, que permiten agregar nombres de formato de varios caracteres.
Algunas aplicaciones (como el servidor HTTP Apache ) incluyen su propia printf
función similar e incorporan extensiones. Sin embargo, todos estos tienden a tener los mismos problemas que register_printf_function()
él.
La función del kernel de Linux printk
admite varias formas de mostrar las estructuras del kernel utilizando la %p
especificación genérica, agregando caracteres de formato adicionales. [8] Por ejemplo, %pI4
imprime una dirección IPv4 en formato decimal con puntos. Esto permite la verificación de cadenas de formato estático (de la %p
porción) a expensas de la compatibilidad total con printf normal.
La mayoría de los lenguajes que tienen una printf
función similar solucionan la falta de esta característica simplemente usando el %s
formato y convirtiendo el objeto en una representación de cadena.
Si se proporcionan muy pocos argumentos de función para proporcionar valores para todas las especificaciones de conversión en la cadena de plantilla, o si los argumentos no son del tipo correcto, los resultados no estarán definidos y el programa puede fallar. Las implementaciones son inconsistentes en cuanto a si los errores de sintaxis en la cadena consumen un argumento y qué tipo de argumento consumen. Se ignoran los argumentos excesivos. En varios casos, el comportamiento indefinido ha dado lugar a vulnerabilidades de seguridad de " ataque de cadena de formato " . En la mayoría de las convenciones de llamadas de C o C++, los argumentos se pueden pasar en la pila, lo que significa que, en el caso de muy pocos argumentos, printf leerá más allá del final del marco de pila actual, permitiendo así que el atacante lea la pila.
Algunos compiladores, como GNU Compiler Collection , verificarán estáticamente las cadenas de formato de funciones similares a printf y advertirán sobre problemas (cuando se usan las banderas -Wall
o -Wformat
). GCC también advertirá sobre funciones de estilo printf definidas por el usuario si __attribute__
se aplica el "formato" no estándar a la función.
Usar solo anchos de campo para la tabulación, como ocurre con un formato como el %8d%8d%8d
de tres números enteros en tres columnas de 8 caracteres, no garantizará que se mantenga la separación de campos si aparecen números grandes en los datos:
1234567 1234567 1234567123 123 123 123 12345678123
La pérdida de separación de campos puede provocar fácilmente resultados corruptos. En los sistemas que fomentan el uso de programas como bloques de construcción en scripts, estos datos corruptos a menudo pueden reenviarse y corromper el procesamiento posterior, independientemente de si el programador original esperaba que el resultado solo fuera leído por ojos humanos. Estos problemas se pueden eliminar incluyendo delimitadores explícitos, incluso espacios, en todos los formatos de salida tabulares. Simplemente cambiar el peligroso ejemplo anterior para %7d %7d %7d
abordar esto, formatear de manera idéntica hasta que los números se hagan más grandes, pero luego evitar explícitamente que se fusionen en la salida debido a los espacios explícitamente incluidos:
1234567 1234567 1234567123 123 123 123 12345678 123
Se aplican estrategias similares a los datos de cadenas.
Aunque es una función de salida en la superficie, printf
permite escribir en una ubicación de memoria especificada por un argumento a través de %n
. Esta funcionalidad se utiliza ocasionalmente como parte de ataques de cadenas de formato más elaborados. [9]
La %n
funcionalidad también hace que Turing sea completoprintf
accidentalmente incluso con un conjunto de argumentos bien formado. Un juego de tres en raya escrito en formato cadena es el ganador del 27º IOCCC . [10]
No se incluyen en esta lista los lenguajes que usan cadenas de formato que se desvían del estilo de este artículo (como AMPL y Elixir ), los lenguajes que heredan su implementación de la JVM u otro entorno (como Clojure y Scala ) y los lenguajes que no lo hacen. no tiene una implementación printf nativa estándar pero tiene bibliotecas externas que emulan el comportamiento de printf (como JavaScript ).
%
del operador) [13]printf
, sprintf
y fmt
)print()
y FileStream.printf()
)printf
comando de utilidad, a veces integrado en el shell, como en algunas implementaciones de KornShell (ksh), Bourne nuevamente shell (bash) o Z shell (zsh). Estos comandos suelen interpretar los escapes de C en la cadena de formato.iostream
printf
(Unix)printk
(imprimir mensajes del kernel)scanf