¿Dónde puedo encontrar el código fuente de las funciones de la biblioteca VC 6.0 C?
/***
*printf.c - Formato de impresión
*
* Copyright (c) 1985 - 1997, Microsoft Corporation.
*
* Propósito:
* Definir printf() - imprimir datos formateados
*< / p>
* Copyright propiedad de Microsoft Corporation. Imprimir datos formateados
*
********************************* ************************************************* **/
#include
#include
#include
#include
#include p>
#include
#include
#include
#include
#incluye
/***
*int printf(formato,...) - Imprimir datos formateados
*
*Propósito:
* Usar cadena de formato imprime los datos formateados en la salida estándar
* Formatee los datos y obtenga tantos argumentos como sea posible para la llamada
* Utilice almacenamiento en búfer temporal para mayor eficiencia.
* _output hace el verdadero trabajo aquí
*
* Entrada.
* char *format - cadena de formato, utilizada para controlar el formato de datos/número de parámetros
*Seguido de la lista de parámetros, el número y tipo de parámetros se ven afectados
* char *format - cadena de formato, utilizada para controlar el formato de datos/número de parámetros
*Seguido de la lista de parámetros, el número y tipo de parámetros se ven afectados
* cadena de formato
*
*Salir:
* Devuelve el número de caracteres impresos
*
*Excepción:
************ ********************** ********** ****************************************/
int __cdecl printf (
const char *formato,
...
)
/*
* stdout ' 'IMPRIMIR'', ''F''formatado
*/
{
va_list arglist; p>
int buffing;
p>
int retval;
va_start(arglist, formato);
_ASSERTE(formato! = NULL ); // Macro de afirmación. Si el puntero de cadena de formato de salida es nulo, esto se afirmará y se informará un error en compilaciones DEBUG.
_lock_str2(1, stdout);
buffing = _stbuf(stdout); //stdout: especifica la salida a la pantalla
retval = _output(stdout, format , arglist);
_ ftbuf(buffing, stdout);
_unlock_str2(1, stdout);
return(retval);
}
Lo anterior es printf()
1. Obtenga parámetros opcionales de la función que contiene parámetros opcionales y opere estos parámetros
typedef char *va_list
void va_start( va_list arg_ptr, prev_param
tipo va_arg( va_list arg_ptr, tipo
void va_end( va_list arg_ptr );
Suponga que la función contiene un parámetro requerido y múltiples parámetros opcionales, donde el parámetro requerido se declara como un tipo de datos general y el valor de la variable se puede obtener a través del nombre del parámetro. Los argumentos opcionales son manejados por las macros va_start, va_arg y va_end (definidas en stdarg.h o varargs.h), es decir, todos los argumentos opcionales se manejan estableciendo un puntero al primer argumento opcional, devolviendo el argumento actual, y Esto es manejado restableciendo el puntero después de devolver los parámetros.
va_start: Proporciona un método conveniente para obtener parámetros para funciones con un número variable de parámetros. Establezca arg_ptr en un puntero al primer argumento opcional pasado a la lista de argumentos de la función, que debe ser de tipo va_list. prev_param es el parámetro requerido antes del primer parámetro opcional en la lista de parámetros.
va_arg: Devuelve el valor del parámetro señalado por arg_ptr e incrementa la dirección que apunta al siguiente parámetro. tipo es el tipo del parámetro actual, que se utiliza para calcular la longitud del parámetro y determinar la posición inicial del siguiente parámetro. Se puede usar varias veces dentro de una función hasta que se obtengan todos los parámetros de la función, pero se debe llamar después de la macro va_start.
va_end: establece el puntero arg_ptr en NULL después de recuperar todos los argumentos.
Aquí tienes un ejemplo:
#include
#include
int promedio(int primero, ...); p>
p>
void main( void )
{
/* Llamado con 3 enteros (-1 se usa como terminador). */
printf( "El promedio es: d\n", promedio( 2, 3, 4, -1);
/* Llama a 4 enteros. */ p >
printf( "El promedio es: d\n", promedio( 5, 7, 9, 11, - 1));
/* Llama a 3 enteros (-1 se usa como terminador). símbolo. 1 );
/* Solo llama a -1 como terminador.
*/
printf( "El promedio es: d\n", promedio( -1)
}
int promedio( int primero, .. )
{
int recuento = 0, suma = 0, i = primero;
marcador va_list;
va_start(marcador. , primero ); /* Inicializa los parámetros variables. = -1 )
{
suma = i
cuenta
i = va_arg(marcador, int); p>
p>
}
va_end(marker); /* Restablecer parámetros variables. */
return( suma ? (suma / recuento): 0
}
Devoluciones:
El promedio es: 3);
El promedio es: 8
El promedio es: 0
En resumen, en la función printf(), solo puede generar una cadena, o puede generar según algunos El formulario genera información de cadena con múltiples parámetros opcionales. Entonces, el primer paso es obtener todos los parámetros opcionales a través de estas macros. Como puede ver en el código fuente anterior para printf(), solo la macro at_start asigna la primera dirección del argumento opcional a arglist.
2. Bloquee la cadena y envíela a la pantalla
#define _lock_str2(i, s) _lock_file2(i, s)
void __cdecl _lock_file2 (int, void *);
#define _unlock_str2(i, s) _unlock_file2(i, s)
void __cdecl _unlock_file2(int, void *); p>int __cdecl _stbuf(FILE *);
void __cdecl _ftbuf(int, FILE *);
int __cdecl _output(FILE *, const char *, va_list); p> p>
En la función de salida, lea cada carácter en la cadena de formato y luego procéselo. El método de procesamiento depende del significado de cada carácter. Por ejemplo: los caracteres ordinarios se utilizan directamente. Función WRITE_CHAR(ch, amp; charsout); Salida a la consola.
La parte más importante es el procesamiento de los descriptores de conversión (d, c, s, f). Esta parte del código ahora se describirá en detalle. Aquí solo se presenta la descripción más básica del descriptor de conversión. se proporciona. , estos deben ser los modificadores básicos para modificar el descriptor de conversión y se procesarán por separado en el programa. Lo siguiente es parte del código fuente de la función salida() (output.c):
case ST_TYPE:
// Indica que el tipo de carácter actualmente procesado es un descriptor de conversión .
...
switch (ch) {
// Los siguientes valores de parámetros se determinan utilizando la macro va_arg(va_list arg_ptr, type) ;.
case ''c'': {
// Obtiene un solo carácter de la lista de parámetros y lo envía a una cadena de búfer, en este caso, type=int
buffer[0] = (char) get_int_arg(amp; argptr); /* Obtener el carácter a imprimir*/
text = buffer
textlen = 1 ; /* Solo imprime un solo carácter*/
}
break;
case ''s'': {
/ // Obtenga la cadena de la lista de parámetros y envíela a la cadena del búfer. En este momento, type=char*
int i;
char *p;* temps */
text = get_ptr_arg(amp; argptr);
...
}
romper;
caso ''w'': {
/ Sí Procesar caracteres amplios
...
}/*Ejemplo ''w''*/
break;
.. .
caso ''e'':
caso ''f'':
caso ''g'': {
// Operar números de coma flotante
...
#if ! LONGDOUBLE_IS_DOUBLE
/* Realizar conversión*/
if (flags amp; FL_LONGDOUBLE) {
_cldcvt((LONGDOUBLE*)argptr, text, ch, precision, capexp);
va_arg(argptr, LONGDOUBLE);
else
#endif / * ! LONGDOUBLE_IS_DOUBLE *
{
///El procesamiento se realiza para tipos de doble precisión, en este punto, type=double
_cfltcvt((DOUBLE*)argptr , texto, ch, precisión, capexp);
va_arg(argptr, DOUBLE);
}
...
break ;
//Procesamiento de variables enteras
case 'd'':
case 'i'':
...
ir a COMMON_INT;
caso ''u'':
radix = 10;
ir a COMMON_INT;
caso ''p'':
...
ir a COMMON_INT;
caso ''o'':
. ..
Nota: Existen descriptores de conversión correspondientes (f para doble, lf para doble largo) para dobles de punto flotante y dobles largos, pero no para números de punto flotante. Una razón para esto es que bajo K&RC, los números de punto flotante se convierten automáticamente al tipo doble antes de usarse en expresiones o como argumentos.
ANSI C, por otro lado, generalmente no convierte automáticamente los números de punto flotante a doble, y algunos programas asumen que los argumentos de punto flotante se convierten a doble, por lo que para proteger una gran cantidad de dichos programas, todos los argumentos de punto flotante se imprimen en printf( ) La función aún convierte automáticamente a tipo doble. Por lo tanto, no se requiere ningún especificador de conversión específico para mostrar tipos de punto flotante, ya sea bajo Kamp RC o ANSI C.
En resumen, el descriptor de conversión debe ser del mismo tipo que el carácter que se imprime. Normalmente, el usuario puede elegir. Por ejemplo, si desea imprimir un valor de tipo int. Sólo se pueden utilizar d, x u o. Todos estos descriptores indican que se va a imprimir un valor de tipo int; simplemente proporcionan varias formas diferentes de representar un valor numérico. Asimismo, f, g y e se pueden utilizar para representar valores de tipo double. Sin embargo, si la especificación de conversión no coincide con el tipo, pueden producirse resultados inesperados. ¿Por qué sucede esto? El problema radica en la forma en que C pasa información a las funciones.
Los detalles básicos de este error son específicos de la implementación. Determina la forma en que se pasan los parámetros en el sistema. La llamada a la función es la siguiente:
float n1;
doble n2;
long n3; >
...
printf("ld, ld, ld, ld", n1, n2, n3, n4);
Esta llamada le dice a la computadora que copie el variables n1, n2, Los valores de n3 y n4 se proporcionan a la computadora, que coloca estas variables en un área de memoria llamada pila. La computadora coloca estos valores en la pila según el tipo de variable en lugar del especificador de conversión, por ejemplo, n1 coloca 8 bytes en la pila (punto flotante convertido a doble), de manera similar, n2 coloca 8 bytes en la pila. , n3 y n4 colocan 4 bytes cada uno en la pila. A continuación, el control se transfiere a printf(); esta función lee datos de la pila, pero en el proceso se guía por el descriptor de conversión y lee el valor. El especificador ld especifica que printf() debe leer 4 bytes (tipo=long en va_arg( va_list arg_ptr, tipo )), por lo que printf() lee 4 bytes de la pila como primer valor. Pero esto es sólo la primera mitad de n1, este valor se trata como un entero largo. La siguiente leyenda ld lee en 4 bytes, exactamente la segunda mitad de n1, que se trata como el segundo entero largo. Asimismo, las leyendas tercera y cuarta se leen nuevamente en las partes antes y después de n2. Entonces, aunque estamos usando los descriptores correctos para n3 y n4, printf() todavía genera un error.
Ver también
http://mirrors.kernel.org/gnu/glibc/glibc-2.7.tar.gz.