¿Qué ha sido del bello arte de leer el excelente manual?
Jens
8
Creo que la verdadera pregunta es ¿cuál es el PUNTO de una opción como esta? ¿Por qué alguien querría saber el valor de los números de caracteres impresos y mucho menos escribir ese valor directamente en la memoria? Era como si los desarrolladores se aburrieran y decidieran introducir un error en el kernel
Menciona que el argumento debe ser un puntero a un int con signo, luego usó un int sin signo en su ejemplo (probablemente solo un error tipográfico).
bta
1
@AndrewS: Porque la función modificará el valor de la variable.
Jack
3
@Jack intsiempre está firmado.
jamesdlin
1
@jamesdlin: Mi error. Lo siento ... No sabía dónde leí eso.
Jack
1
Por alguna razón, la muestra genera un error con nota n format specifies disabled. ¿Cual es la razón?
Johnny_D
186
La mayoría de estas respuestas explican qué %nhace (que es no imprimir nada y escribir el número de caracteres impresos hasta ahora en una intvariable), pero hasta ahora nadie ha dado realmente un ejemplo de qué uso tiene. Acá hay uno:
int n;
printf("%s: %nFoo\n","hello",&n);
printf("%*sBar\n", n,"");
imprimirá:
hello:FooBar
con Foo y Bar alineados. (Es trivial hacer eso sin usar %npara este ejemplo en particular, y en general uno siempre puede romper esa primera printfllamada:
int n = printf("%s: ","hello");
printf("Foo\n");
printf("%*sBar\n", n,"");
Si la conveniencia ligeramente agregada vale la pena usar algo esotérico como %n(y posiblemente introducir errores) está abierta a debate).
¡Oh, esta es una versión basada en caracteres para calcular el tamaño de píxel de la cadena en una fuente dada!
¿Podría explicar por qué se necesitan & n y * s. ¿Son ambos punteros?
Andrew S
9
@AndrewS &nes un puntero ( &es la dirección del operador); es necesario un puntero porque C es un valor de paso y, sin un puntero, printfno podría modificar el valor de n. El %*suso en la printfcadena de formato imprime un %sespecificador (en este caso la cadena vacía "") usando un ancho de campo de ncaracteres. Una explicación de los printfprincipios básicos está básicamente fuera del alcance de esta pregunta (y respuesta); Recomiendo leer la printfdocumentación o hacer su propia pregunta por separado sobre SO.
jamesdlin
3
Gracias por mostrar un caso de uso. No entiendo por qué la gente básicamente copia y pega el manual en SO y lo reformula a veces. Somos humanos y todo se hace por una razón que siempre debe explicarse en una respuesta. "No hace nada" es como decir "La palabra cool significa cool" - conocimiento casi inútil.
the_endian
1
@PSkocik Es enrevesado y propenso a errores sin agregar un nivel adicional de indirección.
jamesdlin
18
Realmente no he visto muchos usos prácticos del %nespecificador en el mundo real , pero recuerdo que se usó en vulnerabilidades de printf de la vieja escuela con un ataque de cadena de formato bastante tiempo.
Algo que fue asi
void authorizeUser(char* username,char* password){...code here setting authorized to false...
printf(username);if( authorized ){
giveControl(username);}}
donde un usuario malintencionado podría aprovechar el parámetro del nombre de usuario que se pasa a printf como la cadena de formato y usar una combinación de %d,%c o w / e para pasar por la pila de llamadas y luego modificar la variable autorizada a un valor verdadero.
Sí, es un uso esotérico, pero ¿siempre es útil saber cuándo escribir un demonio para evitar agujeros de seguridad? :RE
Hay más razones que %npara evitar usar una cadena de entrada sin marcar como una printfcadena de formato.
Keith Thompson
13
Desde aquí vemos que almacena la cantidad de caracteres impresos hasta el momento.
n El argumento será un puntero a un número entero en el que se escribe el número de bytes escritos en la salida hasta ahora por esta llamada a una de las fprintf()funciones. No se convierte ningún argumento.
Un ejemplo de uso sería:
int n_chars =0;
printf("Hello, World%n",&n_chars);
Hasta ahora, todas las respuestas son sobre eso %n, pero no por qué alguien lo querría en primer lugar. Creo que es algo útil con sprintf/ snprintf, cuando es posible que deba dividir o modificar la cadena resultante, ya que el valor almacenado es un índice de matriz en la cadena resultante. Sin embargo, esta aplicación es mucho más útil, sscanfespecialmente porque las funciones en la scanffamilia no devuelven el número de caracteres procesados sino el número de campos.
Otro uso realmente hackeo es obtener un pseudo-log10 gratis al mismo tiempo mientras imprime un número como parte de otra operación.
+1 por mencionar usos para %n, aunque ruego diferir sobre "todas las respuestas ...". = P
jamesdlin
1
Los malos te agradecen por tu uso de printf /% n, sprintf y sscanf;)
jww
66
@noloader: ¿Cómo es eso? El uso de%n tiene absolutamente cero peligro de vulnerabilidad para un atacante. La infamia fuera de lugar %nrealmente pertenece a la estúpida práctica de pasar una cadena de mensaje en lugar de una cadena de formato como argumento de formato. Esta situación, por supuesto, nunca surge cuando en %nrealidad es parte de una cadena de formato intencional que se está utilizando.
R .. GitHub DEJA DE AYUDAR AL HIELO
% n te permite escribir en la memoria. Creo que está asumiendo que el atacante no controla ese puntero (podría estar equivocado). Si el atacante controla el puntero (es solo otro parámetro para imprimir), él / ella podría realizar una escritura de 4 bytes. Si él / ella puede beneficiarse es una historia diferente.
jww
8
@noloader: Eso es cierto sobre cualquier uso de punteros. Nadie dice "chicos malos, gracias" por escribir *p = f();. ¿Por qué debería %n, que es solo otra forma de escribir un resultado al objeto señalado por un puntero, ser considerado "peligroso", en lugar de considerar al puntero como peligroso?
R .. GitHub DEJA DE AYUDAR AL HIELO
10
El argumento asociado con el %nserá tratado como un int*y se rellena con el número total de caracteres impresos en ese punto en el printf.
El otro día me encontré en una situación en la %nque resolvería muy bien mi problema. A diferencia de mi respuesta anterior , en este caso, no puedo idear una buena alternativa.
Tengo un control GUI que muestra un texto específico. Este control puede mostrar parte de ese texto en negrita (o en cursiva, o subrayado, etc.), y puedo especificar qué parte especificando índices de caracteres iniciales y finales.
En mi caso, estoy generando el texto para el control snprintf, y me gustaría que una de las sustituciones se ponga en negrita. Encontrar los índices iniciales y finales de esta sustitución no es trivial porque:
La cadena contiene múltiples sustituciones, y una de las sustituciones es texto arbitrario especificado por el usuario. Esto significa que hacer una búsqueda textual de la sustitución que me interesa es potencialmente ambigua.
La cadena de formato puede estar localizada y puede usar la $extensión POSIX para los especificadores de formato posicional. Por lo tanto, buscar en la cadena de formato original los propios especificadores de formato no es trivial.
El aspecto de localización también significa que no puedo dividir fácilmente la cadena de formato en varias llamadas a snprintf.
Por lo tanto, la forma más sencilla de encontrar los índices en torno a una sustitución particular sería hacer:
Te daré +1 por el caso de uso. Pero va a fallar una auditoría, por lo que probablemente debería idear otra forma de marcar el comienzo y el final del texto en negrita. Parece que tres snprintfal verificar los valores de retorno funcionarán bien ya que snprintfdevuelve el número de caracteres escritos. Tal vez algo como: int begin = snprintf(..., "blah blah %s %f yada yada", ...);y int end = snprintf(..., "%s", ...);y luego la cola: snprintf(..., "blah blah");.
jww
3
@jww El problema con las snprintfllamadas múltiples es que las sustituciones pueden reorganizarse en otras configuraciones regionales, por lo que no se pueden dividir así.
jamesdlin
Gracias por el ejemplo Pero, ¿no podría, por ejemplo, escribir una secuencia de control de terminal para poner la salida en negrita justo antes del campo y luego escribir una secuencia después? Si no codifica las secuencias de control del terminal, también puede hacer que sean posicionales (reorderables).
PSkocik
1
@PSkocik Si está enviando a una terminal. Si está trabajando con, por ejemplo, un control de edición enriquecida de Win32, eso no ayudará a menos que desee volver y analizar las secuencias de control de terminal después. Eso también supone que desea respetar las secuencias de control de terminal en el resto del texto sustituido; Si no lo hace, entonces tendría que filtrar o escapar de ellos. No digo que sea imposible prescindir de él %n; Estoy afirmando que usar %nes más sencillo que las alternativas.
jamesdlin
1
No imprime nada. Se utiliza para determinar cuántos caracteres se imprimieron antes de que %naparecieran en la cadena de formato y enviarlos al int proporcionado:
#include<stdio.h>int main(int argc,char* argv[]){int resultOfNSpecifier =0;
_set_printf_count_output(1);/* Required in visual studio */
printf("Some format string%n\n",&resultOfNSpecifier);
printf("Count of chars before the %%n: %d\n", resultOfNSpecifier);return0;}
cuando trato de codificar me da: ¡Hola, Caracteres Mundiales impresos hasta ahora = 36 ,,,,, por qué 36?! Yo uso un GCC de 32 bits en una máquina con Windows.
%nexistió en C89. No funciona con MSVC porque Microsoft lo deshabilitó de manera predeterminada por cuestiones de seguridad; _set_printf_count_outputprimero debe llamar para habilitarlo. (Ver la respuesta de Merlyn Morgan-Graham.)
Estás simplemente equivocado. Se enumera claramente en la Tabla B-1 ( printfconversiones) del Apéndice B de K&R, segunda edición. (Página 244 de mi copia). O consulte la sección 7.9.6.1 (página 134) de la especificación ISO C90.
%n
las marcasprintf
accidentalmente Turing completo y se puede aplicar por ejemplo Brainfuck en ella, ver github.com/HexHive/printbf y oilshell.org/blog/2019/02/07.html#appendix-a-minor-sublanguagesRespuestas:
Nada impreso El argumento debe ser un puntero a un int con signo, donde se almacena el número de caracteres escritos hasta ahora.
Se imprime el código anterior:
fuente
int
siempre está firmado.n format specifies disabled
. ¿Cual es la razón?La mayoría de estas respuestas explican qué
%n
hace (que es no imprimir nada y escribir el número de caracteres impresos hasta ahora en unaint
variable), pero hasta ahora nadie ha dado realmente un ejemplo de qué uso tiene. Acá hay uno:imprimirá:
con Foo y Bar alineados. (Es trivial hacer eso sin usar
%n
para este ejemplo en particular, y en general uno siempre puede romper esa primeraprintf
llamada:Si la conveniencia ligeramente agregada vale la pena usar algo esotérico como
%n
(y posiblemente introducir errores) está abierta a debate).fuente
&n
es un puntero (&
es la dirección del operador); es necesario un puntero porque C es un valor de paso y, sin un puntero,printf
no podría modificar el valor den
. El%*s
uso en laprintf
cadena de formato imprime un%s
especificador (en este caso la cadena vacía""
) usando un ancho de campo den
caracteres. Una explicación de losprintf
principios básicos está básicamente fuera del alcance de esta pregunta (y respuesta); Recomiendo leer laprintf
documentación o hacer su propia pregunta por separado sobre SO.Realmente no he visto muchos usos prácticos del
%n
especificador en el mundo real , pero recuerdo que se usó en vulnerabilidades de printf de la vieja escuela con un ataque de cadena de formato bastante tiempo.Algo que fue asi
donde un usuario malintencionado podría aprovechar el parámetro del nombre de usuario que se pasa a printf como la cadena de formato y usar una combinación de
%d
,%c
o w / e para pasar por la pila de llamadas y luego modificar la variable autorizada a un valor verdadero.Sí, es un uso esotérico, pero ¿siempre es útil saber cuándo escribir un demonio para evitar agujeros de seguridad? :RE
fuente
%n
para evitar usar una cadena de entrada sin marcar como unaprintf
cadena de formato.Desde aquí vemos que almacena la cantidad de caracteres impresos hasta el momento.
Un ejemplo de uso sería:
n_chars
entonces tendría un valor de12
.fuente
Hasta ahora, todas las respuestas son sobre eso
%n
, pero no por qué alguien lo querría en primer lugar. Creo que es algo útil consprintf
/snprintf
, cuando es posible que deba dividir o modificar la cadena resultante, ya que el valor almacenado es un índice de matriz en la cadena resultante. Sin embargo, esta aplicación es mucho más útil,sscanf
especialmente porque las funciones en lascanf
familia no devuelven el número de caracteres procesados sino el número de campos.Otro uso realmente hackeo es obtener un pseudo-log10 gratis al mismo tiempo mientras imprime un número como parte de otra operación.
fuente
%n
, aunque ruego diferir sobre "todas las respuestas ...". = P%n
tiene absolutamente cero peligro de vulnerabilidad para un atacante. La infamia fuera de lugar%n
realmente pertenece a la estúpida práctica de pasar una cadena de mensaje en lugar de una cadena de formato como argumento de formato. Esta situación, por supuesto, nunca surge cuando en%n
realidad es parte de una cadena de formato intencional que se está utilizando.*p = f();
. ¿Por qué debería%n
, que es solo otra forma de escribir un resultado al objeto señalado por un puntero, ser considerado "peligroso", en lugar de considerar al puntero como peligroso?El argumento asociado con el
%n
será tratado como unint*
y se rellena con el número total de caracteres impresos en ese punto en elprintf
.fuente
El otro día me encontré en una situación en la
%n
que resolvería muy bien mi problema. A diferencia de mi respuesta anterior , en este caso, no puedo idear una buena alternativa.Tengo un control GUI que muestra un texto específico. Este control puede mostrar parte de ese texto en negrita (o en cursiva, o subrayado, etc.), y puedo especificar qué parte especificando índices de caracteres iniciales y finales.
En mi caso, estoy generando el texto para el control
snprintf
, y me gustaría que una de las sustituciones se ponga en negrita. Encontrar los índices iniciales y finales de esta sustitución no es trivial porque:La cadena contiene múltiples sustituciones, y una de las sustituciones es texto arbitrario especificado por el usuario. Esto significa que hacer una búsqueda textual de la sustitución que me interesa es potencialmente ambigua.
La cadena de formato puede estar localizada y puede usar la
$
extensión POSIX para los especificadores de formato posicional. Por lo tanto, buscar en la cadena de formato original los propios especificadores de formato no es trivial.El aspecto de localización también significa que no puedo dividir fácilmente la cadena de formato en varias llamadas a
snprintf
.Por lo tanto, la forma más sencilla de encontrar los índices en torno a una sustitución particular sería hacer:
fuente
snprintf
al verificar los valores de retorno funcionarán bien ya quesnprintf
devuelve el número de caracteres escritos. Tal vez algo como:int begin = snprintf(..., "blah blah %s %f yada yada", ...);
yint end = snprintf(..., "%s", ...);
y luego la cola:snprintf(..., "blah blah");
.snprintf
llamadas múltiples es que las sustituciones pueden reorganizarse en otras configuraciones regionales, por lo que no se pueden dividir así.%n
; Estoy afirmando que usar%n
es más sencillo que las alternativas.No imprime nada. Se utiliza para determinar cuántos caracteres se imprimieron antes de que
%n
aparecieran en la cadena de formato y enviarlos al int proporcionado:( Documentación para
_set_printf_count_output
)fuente
Almacenará el valor del número de caracteres impresos hasta ahora en esa
printf()
función.Ejemplo:
La salida de este programa será
fuente
% n es C99, no funciona con VC ++.
fuente
%n
existió en C89. No funciona con MSVC porque Microsoft lo deshabilitó de manera predeterminada por cuestiones de seguridad;_set_printf_count_output
primero debe llamar para habilitarlo. (Ver la respuesta de Merlyn Morgan-Graham.)printf
conversiones) del Apéndice B de K&R, segunda edición. (Página 244 de mi copia). O consulte la sección 7.9.6.1 (página 134) de la especificación ISO C90._set_printf_count_output