¿Cuál será el resultado si imprimimos una cadena que contiene "% s"?
printf undefined (5)
Me gustaría saber cuál sería el resultado si imprimiéramos una cadena que contiene "% s" en su contenido.
Intenté imprimirlo como "hola% s".
char *ptr="hi%s";
printf(ptr);
Esperaba que la salida fuera como "hola". pero lo obtuve como "hola hola% s".
"hola hola% s"
Como han mencionado otras respuestas, lo que está haciendo invoca
un comportamiento indefinido
porque está llamando a
printf
sin suficientes argumentos para el especificador de formato dado.
En su caso, obtuvo resultados no deseados, pero su programa podría haberse bloqueado con la misma facilidad.
Para ampliar esto, código que se ve así:
printf(ptr);
Casi siempre está mal.
Si la cadena a la que apunta
ptr
puede ser controlada por el usuario de alguna manera, usted se abre a una
vulnerabilidad de formato de cadena
.
El usuario podría suministrar una cadena especialmente diseñada con
%s
,
%x
, etc. que podría volcar el contenido de la memoria y exponer datos confidenciales, o una cadena con
%n
que podría escribir en la memoria y permitir que un atacante se haga cargo de su programa.
Muchos analizadores estáticos lanzarán una advertencia si el primer argumento para
printf
no es una constante de cadena por este motivo.
Como se especifica en otra respuesta, se producirá un comportamiento indefinido.
Qué significa el comportamiento indefinido en este contexto:
cuando
printf
recibe una cadena con
n
número de especificadores de formato (como% s), va a esperar que se pase
n
número de parámetros a la función además de la cadena.
Entonces, cuando tiene una declaración como esta
printf("hi%s")
, la función se comportará como si pasara un parámetro (en este caso, el segundo parámetro debería ser un char *) aunque no lo haya.
La función solo va a obtener el siguiente valor en la pila, que es algún valor basura en este caso.
Entonces la función hará referencia a este valor basura y lo tratará como un búfer de caracteres.
Las razones por las que este comportamiento es indefinido es que no se sabe cuál podría ser el valor basura en la pila.
Posibles resultados
-
El valor basura en la pila es 0 -> Fallo de segmentación cuando se desreferencia el valor basura.
-
El valor basura en la pila resulta ser una dirección de memoria válida -> Los bytes en la ubicación de la memoria se seguirán insertando en la cadena (anexada a "hola" en este caso) hasta que se encuentre un byte de valor 0 o una segmentación la falla ocurre.
- El valor basura en la pila no es 0 pero no es una ubicación de memoria válida -> Error de segmentación cuando el valor basura se desreferencia.
Producir la misma situación
La siguiente parte del código es una situación muy similar a
printf("hi%s")
void foo() {
char * myJunkValue; // Since not a global/static variable, this is not guaranteed to be 0
printf(myJunkValue); // Undefined behavior
}
Habrá un comportamiento indefinido. :)
Esta parte del literal de cadena
%s
es considerada por la función como un especificador de formato.
Desde el estándar C (7.21.6.1 La función fprintf)
- ... Si no hay argumentos suficientes para el formato, el comportamiento es indefinido.
Si desea generar la cadena
"hi%s"
como está, debe definir el literal como
"hi%%s"
.
Aquí hay un programa demostrativo
#include <stdio.h>
int main(void)
{
char *ptr = "hi%%s";
printf( ptr );
return 0;
}
Su salida es
hi%s
No estás "imprimiendo una cadena que tiene
%s
en su contenido".
Está pasando una cadena como la
cadena de formato
a
printf
, y al hacerlo sin un argumento coincidente para el campo de formato, su programa tiene
un comportamiento indefinido
.
El primer argumento para
printf
no es una cadena que desea imprimir.
Es una cadena de formato que controla cómo se interpretan / convierten los argumentos restantes, y que también puede contener texto literal para fusionarlos.
"La impresión de una cadena que tiene
%s
en su contenido" (donde
ptr
apunta a esa cadena, como en su pregunta) puede lograrse mediante
printf("%s", ptr)
o
puts(ptr)
.
Su programa invoca un comportamiento indefinido .
Tu código es equivalente a
printf("hi%s");
donde
%s
es un especificador de conversión y espera que se proporcione un argumento.
Citando
C11
, capítulo §7.21.6.1
[...] Si no hay argumentos suficientes para el formato, el comportamiento es indefinido. [....]
Sugerencia:
si solo tiene que imprimir una cadena, sin necesidad de conversión (formateo), puede usar
puts()
.