Tengo una función que acepta una cadena, es decir:
void log_out(char *);
Al llamarlo, necesito crear una cadena formateada sobre la marcha como:
int i = 1;
log_out("some text %d", i);
¿Cómo hago esto en ANSI C?
Solo que, dado que sprintf()
devuelve un int, esto significa que tengo que escribir al menos 3 comandos, como:
char *s;
sprintf(s, "%d\t%d", ix, iy);
log_out(s);
¿Alguna forma de acortar esto?
Respuestas:
Utilice sprintf .
Parámetros:
Ejemplo:
fuente
Si tiene un sistema compatible con POSIX-2008 (cualquier Linux moderno), puede usar la función segura y conveniente
asprintf()
: tendrámalloc()
suficiente memoria para usted, no necesita preocuparse por el tamaño máximo de cadena. Úselo así:Este es el esfuerzo mínimo que puede realizar para construir la cuerda de forma segura. El
sprintf()
código que dio en la pregunta es profundamente defectuoso:No hay memoria asignada detrás del puntero. ¡Está escribiendo la cadena en una ubicación aleatoria en la memoria!
Incluso si hubieras escrito
estaría en serios problemas, porque no puede saber qué número poner entre paréntesis.
Incluso si hubiera utilizado la variante "segura"
snprintf()
, aún correría el peligro de que sus cadenas se truncaran. Al escribir en un archivo de registro, esa es una preocupación relativamente menor, pero tiene el potencial de cortar con precisión la información que hubiera sido útil. Además, cortará el carácter final de la línea final, pegando la siguiente línea de registro al final de su línea escrita sin éxito.Si intenta usar una combinación de
malloc()
ysnprintf()
producir un comportamiento correcto en todos los casos, terminará con aproximadamente el doble de código del que he dadoasprintf()
y básicamente reprogramará la funcionalidad deasprintf()
.Si está buscando proporcionar un contenedor
log_out()
que pueda tomar unaprintf()
lista de parámetros de estilo, puede usar la variantevasprintf()
que toma ava_list
como argumento. Aquí hay una implementación perfectamente segura de dicho contenedor:fuente
asprintf()
no forma parte del estándar C 2011 ni de POSIX, ni siquiera POSIX 2008 o 2013. Forma parte de TR 27431-2: consulte ¿Utiliza las funciones 'seguras' de TR 24731?Me parece que desea poder pasar fácilmente una cadena creada con el formato de estilo printf a la función que ya tiene que toma una cadena simple. Puede crear una función contenedora utilizando las
stdarg.h
instalaciones yvsnprintf()
(que pueden no estar disponibles, dependiendo de su compilador / plataforma):Para las plataformas que no proporcionan una buena implementación (o ninguna implementación) de la
snprintf()
familia de rutinas, he utilizado con éxito un dominio casi públicosnprintf()
de Holger Weiss .fuente
Si tiene el código
log_out()
, vuelva a escribirlo. Lo más probable es que puedas hacer:Si se necesita información de registro adicional, se puede imprimir antes o después del mensaje que se muestra. Esto ahorra asignación de memoria y tamaños de búfer dudosos y así sucesivamente. Probablemente necesite inicializar
logfp
a cero (puntero nulo) y verificar si es nulo y abrir el archivo de registro según corresponda, pero el código existentelog_out()
debería lidiar con eso de todos modos.La ventaja de esta solución es que simplemente puede llamarla como si fuera una variante de
printf()
; de hecho, es una variante menor deprintf()
.Si no tiene el código
log_out()
, considere si puede reemplazarlo con una variante como la descrita anteriormente. Si puede usar el mismo nombre dependerá de su marco de aplicación y de la fuente última de lalog_out()
función actual . Si está en el mismo archivo de objeto que otra función indispensable, tendrá que usar un nuevo nombre. Si no puede averiguar cómo replicarlo exactamente, tendrá que usar alguna variante como las que se dan en otras respuestas que asignen una cantidad adecuada de memoria.Obviamente, ahora llama al en
log_out_wrapper()
lugar delog_out()
, pero la asignación de memoria y así sucesivamente se realiza una vez. Me reservo el derecho de sobreasignar espacio por un byte innecesario; no he verificado dos veces si la longitud devuelta porvsnprintf()
incluye el nulo de terminación o no.fuente
No use sprintf.
Desbordará su String-Buffer y bloqueará su programa.
Utilice siempre snprintf
fuente
No he hecho esto, así que solo voy a señalar la respuesta correcta.
C tiene disposiciones para funciones que toman números no especificados de operandos, usando el
<stdarg.h>
encabezado. Puede definir su función comovoid log_out(const char *fmt, ...);
y obtener elva_list
interior de la función. Luego, puede asignar memoria y llamarvsprintf()
con la memoria asignada, formato yva_list
.Alternativamente, puede usar esto para escribir una función análoga a la
sprintf()
que asignaría memoria y devolvería la cadena formateada, generándola más o menos como arriba. Sería una pérdida de memoria, pero si acaba de cerrar la sesión, puede que no importe.fuente
http://www.gnu.org/software/hello/manual/libc/Variable-Arguments-Output.html da el siguiente ejemplo para imprimir en stderr. Puede modificarlo para usar su función de registro en su lugar:
En lugar de vfprintf, deberá usar vsprintf donde debe proporcionar un búfer adecuado para imprimir.
fuente