especificadores - Pasando byte nulo a través del especificador de formato en `printf`
formato de printf en c (4)
En resumen: %c
significa imprimir un carácter, por lo que printf
imprime el carácter NUL
cuyo valor es 0. NUL
es un carácter no imprimible. Así que solo podemos ver un espacio allí.
"Hello / 0, world" es una cadena literal, el resultado de strlen("Hello/0, world")
es 5. Por lo tanto, printf
imprimirá el resultado "Hello".
Puedes ver más en el sitio web cppreference: cadena literal
Un literal de cadena de caracteres es una secuencia de cero o más caracteres multibyte entre comillas dobles, como en "xyz". El carácter nulo (''/ 0'') siempre se añade al literal de cadena, por lo tanto, un literal de cadena "Hola" es un carácter constante [6] que contiene los caracteres ''H'', ''e'', ''l'', ''l'' , ''0'', y ''/ 0''. Si un literal de cadena tiene caracteres nulos incrustados, representa una matriz que contiene más de una cadena.
¿Por qué printf
imprime un espacio en lugar de detenerse cuando uso el carácter NULO de la tabla ASCII? Esto es lo que quiero decir:
printf("Hello%c, world", 0); //Hello , world
printf("Hello%c, world", ''/0''); //Hello , world
Solo cuando coloco el carácter de escape en la propia cadena, printf
detiene la cadena:
printf("Hello/0, world"); //Hello
Intenté esto en Windows 8, Windows 10 (usando cygwin, MinGW, Netbeans, Code :: Blocks), XUbuntu, es lo mismo.
¿Dónde está el problema? Le pregunté a uno de mis amigos, pero él dijo que no tenía tal problema, que los tres ejemplos se ejecutaron de la misma manera.
Está tomando una dependencia en un detalle de implementación de printf (). La función de salida de terminal de bajo nivel requiere la longitud de la cadena como argumento. Hay dos formas para que printf () haga esto.
La forma algo obvia es primero formatear la cadena, luego usar strlen (). Ese es el que esperabas.
Pero eso es ineficiente porque requiere un doble paso a través del búfer de cadenas y la adición de 0. La otra forma de hacerlo es rastrear la longitud de la cadena con formato al sustituir los campos, simplemente incrementándola por cada carácter agregado. Dado que continúa pasando el% c, ahora obtendrá la mayor longitud que incluye todo el pasado% c. Lo que la función de terminal hace con el 0 incorporado es también un detalle de implementación, dado que no es un carácter imprimible. Verlo sustituido con un espacio no es infrecuente.
La mejor manera de hacer esto es no confiar en los detalles de la implementación.
printf("Hello/0, world");
utiliza su parámetro como una cadena C, por lo que lo descodifica hasta que encuentra un carácter NUL, por lo que se detiene justo después de /0
, ignorando lo que sigue.
printf("Hello%c, world", 0);
descodifica su parámetro (hasta que encuentra dentro de él un carácter NUL - es decir, después de d
), mientras tanto encuentra un %c
, por lo que lo reemplaza con el carácter dado como parámetro (cuyo código ASCII es NUL) y luego lo envía al terminal Un personaje de NUL, y luego continúa.
El manual de Printf dice:
Estas funciones escriben la salida bajo el control de una cadena de formato que especifica cómo se convierten los argumentos subsiguientes [...] para la salida.
printf("Hello%c, world", 0); //Hello , world
printf("Hello%c, world", ''/0''); //Hello , world
En ambos casos, está intentando imprimir el valor de carácter correspondiente al código de carácter 0
, que no es un carácter imprimible. No he encontrado el capítulo y el verso en él, pero sospecho que el comportamiento de intentar imprimir un valor de carácter nulo no está especificado o tal vez incluso no definido. De cualquier manera, no esperaría que se tratara como un terminador de cadena en este caso.
printf("Hello/0, world"); //Hello
En este caso, el carácter nulo es parte de la constante de cadena y el compilador lo interpreta como un terminador de cadena.