¿Cuál es la diferencia entre las familias de funciones printf y vprintf, y cuándo debo usar una sobre la otra?

Entiendo que la diferencia entre las fprintf printf , fprintf , sprintf etc. y las funciones vprintf , vfprintf , vsprintf , etc. tiene que ver con la forma en que tratan los argumentos de la función. Pero, ¿qué tan específicamente? ¿Hay realmente alguna razón para usar uno sobre el otro? ¿Debo usar siempre printf ya que es algo más común de ver en C, o hay una razón legítima para elegir vprintf ?

printf() y sus amigos son para uso normal. vprintf() y los amigos son para cuando quieres escribir tu propia printf() similar a printf() . Digamos que quieres escribir una función para imprimir errores:

 int error(char *fmt, ...) { int result; va_list args; va_start(args, fmt); // what here? va_end(args); return result; } 

Notará que no puede pasar args a printf() , ya que printf() toma muchos argumentos, en lugar de un va_list argumento va_list . Sin embargo, las funciones vprintf() toman un argumento va_list en lugar de un número variable de argumentos, así que aquí está la versión completa:

 int error(char *fmt, ...) { int result; va_list args; va_start(args, fmt); fputs("Error: ", stderr); result = vfprintf(stderr, fmt, args); va_end(args); return result; } 

Nunca desea utilizar vprintf() directamente, pero es increíblemente útil cuando necesita, por ejemplo, envolver printf() . Para estos casos, definirá la función de nivel superior con argumentos variables (…). Luego, los recostackrá en una va_list , realizará el procesamiento y, finalmente, llamará a vprintf() en la va_list para obtener la impresión.

La principal dificultad con los argumentos variad no es que haya un número variable de argumentos sino que no hay un nombre asociado con cada argumento. Las macros va_start, va_arg analizan los argumentos en la memoria (en la mayoría de los comstackdores C que están en la stack) utilizando la información de tipo contenida en la cadena de formato cf. Kernighan y Ritchie, segunda edición, sección 7.3.