¿Cómo se implementa la función printf?
getchar() es equivalente a getc(stdin).
printf tiene una serie de funciones de subimpresión
printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - conversión de salida formateada
Sus declaraciones En un archivo de encabezado separado
#include lt;stdio.hgt;
int printf(const char *format, ...);
int fprintf (ARCHIVO *flujo, const char *formato, ...);
int sprintf(char *str, const char *formato, ...). ...);
int snprintf(char *str, size_t tamaño, const char *formato, ...); >
int vprintf(const char *formato, va_list ap);
int vfprintf(FILE *stream, const char *formato, va_list ap);
int vsprintf( char *str, const char *formato, va_list ap);
int vsnprintf (char *str, size_t tamaño, const char *formato, va_list ap); ():
Estas dos funciones son funciones nuevas en C99. Preste atención a -std=c99 al compilar
Antes de implementar printf, es mejor "revisarlo" primero. Como preludio
Vale la pena señalar que printf no es la única función en C99.
int printf(const char *format, ...);
El valor de retorno es int, el primer parámetro es un puntero de cadena constante y el segundo parámetro es un... ..
Primero, veamos el valor de retorno int
Valor de retorno
Si tienen éxito, estas funciones devolverán el número de caracteres impresos (excluyendo el byte nulo final para salida de cadena).
Umph. Devuelve el número de caracteres impresos correctamente, excluyendo NULL
demo:
#stdio.hgt; main()
{
int contador = 0;
contador = printf("¡hola mundo! d\n", 10);
printf("el contador es d\n", contador);
devuelve 0;
}
jasonleaster@ubuntu:~$ ./a .out
¡hola mundo! 10
el contador es 16
A continuación, el primer parámetro es un puntero a una cadena constante
El formato de la cadena de formato
La cadena de formato es una cadena que comienza y termina con su estado de cambio inicial (si corresponde). La cadena de formato consta de
cero o más instrucciones: caracteres ordinarios (no), que se copian sin cambios en una especificación de conversión,
cada uno El resultado de cada especificación de conversión; es obtener cero o más caracteres. Cada especificación de conversión es introducida por el carácter
y finaliza con una especificación de conversión. En el medio puede haber (en ese orden) cero o más indicadores, un ancho de campo mínimo opcional, una precisión opcional y un modificador de longitud opcional.
Pocas personas utilizarán lo siguiente
.
La gente utilizará el siguiente método
printf("*d", 10, 5);
La primera vez que me encontré con esta situación, puede ser Dijo que estaba "sorprendido" por el contenido impreso. Me llevó mucho tiempo, pero finalmente lo logré. Aquí hay un resumen
Consta de cero o más directivas: caracteres ordinarios (no), que se copian sin cambios en el flujo de salida; y especificaciones de conversión, cada una de las cuales da como resultado cero o más argumentos posteriores. Cada especificación de conversión es introducida por el carácter y termina con una especificación de conversión. Cada especificación de conversión es introducida por el carácter y termina con una especificación de conversión. Entre estos parámetros, puede haber (en este orden) cero o más indicadores, un ancho de campo mínimo opcional, una precisión opcional y
Los parámetros deben corresponder correctamente a la especificación de conversión (después del tipo promoción). De forma predeterminada, los argumentos se utilizan en el orden indicado, pero no en los especificadores de conversión. Los argumentos se utilizan en el orden indicado, donde cada "*" y cada especificador de conversión requiere el siguiente argumento (se produce un error si no hay suficientes especificadores de conversión). También podemos especificar explícitamente qué parámetro usar cuando se necesita un parámetro, usando "m$" en lugar de "" y "*m$" en lugar de "*", donde el entero decimal m representa el número del parámetro requerido en la lista de parámetros. Posición, el índice comienza desde 1. Entonces,
printf("*d", ancho, num), printf("", ancho, num).*d", ancho, num);
y p> p>
printf("2$*1$d", ancho, num);
es equivalente. El estándar C99 no permite referencias repetidas al mismo parámetro ". Especificación UNIX única. Si usa un estilo que usa "$", debe usarlo en todas las conversiones que toman parámetros y en todos los parámetros de ancho y precisión, pero se puede usar con el formato "" que no toma parámetros.
No debe haber espacios en el número de parámetros especificados usando "$", por ejemplo, si se especifican los parámetros 1 y 3, el parámetro 2
también debe estar en algún lugar del formato; especificado.
El tercer parámetro...
Bien, este parámetro es un poco bastardo, se llama parámetro de longitud variable. Una vez hecho esto, el lenguaje C mejorará un poco
Busqué este stdarg.h en el GCC actual y en el kernel Linux 3.0 actual durante mucho tiempo, pero no pude encontrarlo, así que Supongo que está encapsulado en algún lugar...
__builtin_va_start(v, l) El hilo murió aquí. Después de eso ya no puedo encontrar la definición de __builtin_va_start
Es mejor mirar la implementación de los primeros kernels
stdarg.h del kernel 0.12
#ifndef _STDARG_H
#define _STDARG_H
typedef char *va_list;
/* El espacio requerido para TYPE tipo arg en la lista de parámetros.
TIPO también puede ser una expresión cuyo tipo ya esté utilizado. */
#define __va_rounded_size(TIPO) \
(((tamaño de (TIPO) tamaño de (int) - 1) / tamaño de (int))* tamaño de (int))
#ifndef __sparc __
#define va_start(AP, LASTARG) \
(AP = ((char *) amp; (LASTARG) __va_rounded_size (LASTARG) ))
#else
#define va_ start(AP, LASTARG) \
(__builtin_saveregs (), \
AP = ((char *) amp; (LASTARG) __va_rounded_size (LASTARG)))
#endif
void va_end (va_list) /* definido en gnulib */
#define va_end(AP)
#define va_arg(AP, TIPO) (AP = __va_rounded_size (TIPO),
*((TIPO *) (AP - __va_rounded_size ( TIPO ))))
#endif /* _STDARG_H */
va_list es un puntero a una cadena
Analice la definición de macro anterior #define __va_ rounded_size( TYPE ) \
(((sizeof (TYPE) sizeof (int) - 1) / sizeof (int)))* sizeof (int))
Esto se usa para obtener TYPE El tamaño en bytes del tipo de elemento, si tiene menos de 4 bytes (por ejemplo, short y char), el tamaño del elemento se trata como 4 bytes. Si tiene menos de 4 bytes (como short y char), se considera que el tamaño del elemento es de 4 bytes. En pocas palabras, se detecta el tamaño del elemento, y si tiene menos de 4 bytes, se considera. ser de 4 bytes.
#define va_start(AP, LASTARG)\
(AP = ((char *) amp; (LASTARG) __va_rounded_size (LASTARG)))
AP suele ser una va_list y LASTARG Es un puntero a una función con parámetros de longitud variable.
El papel de va_start es obvio. Obtenga la dirección del primer parámetro en la lista de parámetros de longitud variable.
va_end es un puntero a va_list y está establecido en 0 (lo descubrí buscando en Google pero no puedo encontrar la definición de va_end, es solo un #define, así que no puedo hacer nada con él ).
Pero si conoces va_start y va_end, puedes usarlos
Aquí tienes una demostración de parámetros de longitud variable
/****** ** ************************************************* * *************** *******
Escritor del código: EOF
Fecha del código: 2014.04.26
correo electrónico: jasonleaster@gmail.com
Propósito del código:
Solo una demostración de una función de parámetro variable.
Uso: va_sum(un número, otro número...., 0
va_sum(1, 2, 3, 4, 5, 0); p>
********************************************* *** **********************************/
#include lt;stdarg.hgt;
#include lt; stdio.hgt;
int va_sum(int* a,...
int main()
{
int número = 1;
int foo = 0;
foo = va_sum(amp; número, 2, 3, 4, 5, 0 );
devolver 0
}
int va_sum(int* a,...)
{
int contador = 0;
int elemento = 0;
va_list arg; > while((elemento = va_arg(arg, int)) != 0)
{
contador = elemento;
}
va_end(arg);
contador de retorno;
}
#define va_arg(AP, TYPE) \
( AP = __va_rounded_size (TIPO), \
*((TIPO *) (AP - __va_rounded_size (TIPO))))))