tipos tabla size_t programacion operadores lenguaje funcion enteros derivados datos c compiler-construction printf scanf

tabla - tipos de datos enteros en programacion



¿Por qué tengo que especificar el tipo de datos cada vez en C? (11)

Como puede ver en el siguiente fragmento de código, he declarado una variable char y una variable int . Cuando se compila el código, debe identificar los tipos de datos de las variables str e i .

¿Por qué necesito volver a decir durante el escaneo de mi variable que es una cadena o una variable entera al especificar %s o %d a scanf ? ¿No es el compilador lo suficientemente maduro como para identificarlo cuando declare mis variables?

#include <stdio.h> int main () { char str [80]; int i; printf ("Enter your family name: "); scanf ("%s",str); printf ("Enter your age: "); scanf ("%d",&i); return 0; }


¿El compilador no maduró lo suficiente para identificarlo cuando declare mi variable?

No.

Estás usando un lenguaje especificado hace décadas. No espere estética de diseño moderno de C, porque no es un lenguaje moderno. Los lenguajes modernos tenderán a intercambiar una pequeña cantidad de eficiencia en la compilación, interpretación o ejecución para mejorar la usabilidad o la claridad. C proviene de un momento en que el tiempo de procesamiento de la computadora era caro y en un suministro muy limitado, y su diseño refleja esto.

También es por eso que C y C ++ siguen siendo los idiomas preferidos cuando realmente, realmente te importa ser rápido, eficiente o cercano al metal.


Debido a que en el archivo printf no está especificando el tipo de datos, está especificando el formato de datos. Esta es una distinción importante en cualquier idioma, y ​​es doblemente importante en C.

Cuando escanea en una cadena con %s , no está diciendo "analizar una entrada de cadena para mi variable de cadena". No se puede decir eso en C porque C no tiene un tipo de cadena. Lo más parecido que C tiene a una variable de cadena es una matriz de caracteres de tamaño fijo que contiene un carácter que representa una cadena, con el final de la cadena indicado por un carácter nulo. Entonces, lo que realmente está diciendo es "aquí hay una matriz para mantener la cadena, prometo que es lo suficientemente grande como para la entrada de cadena que quiero que analice".

¿Primitivo? Por supuesto. C se inventó hace más de 40 años, cuando una máquina típica tenía como máximo 64 K de RAM. En ese entorno, la conservación de RAM tenía una prioridad más alta que la sofisticada manipulación de cadenas.

Aún así, el escáner %s persiste en entornos de programación más avanzados, donde hay tipos de datos de cadena. Porque se trata de escanear, no escribir.


El compilador puede ser inteligente, pero las funciones printf o scanf son estúpidas: no saben cuál es el tipo de parámetro que pasas para cada llamada. Es por eso que necesita pasar %s o %d cada vez.


El primer parámetro es una cadena de formato . Si está imprimiendo un número decimal, puede verse así:

  • "%d" (número decimal)
  • "%5d" (número decimal rellenado al ancho 5 con espacios)
  • "%05d" (número decimal completado hasta el ancho 5 con ceros)
  • "%+d" (número decimal, siempre con un signo)
  • "Value: %d/n" (algún contenido antes / después del número)

etc., vea por ejemplo Dar formato a marcadores de posición en Wikipedia para tener una idea de lo que las cadenas de formato pueden contener.

También puede haber más de un parámetro aquí:

"%s - %d" (una cadena, luego algo de contenido, luego un número)


Es porque esta es la única forma de decirle a las funciones (como printf scanf ) qué tipo de valor está aprobando. por ejemplo-

int main() { int i=22; printf("%c",i); return 0; }

este código imprimirá el carácter no entero 22. porque le indicó a la función printf que trate la variable como char.


GCC (y posiblemente otros compiladores de C) realizan un seguimiento de los tipos de argumentos, al menos en algunas situaciones. Pero el lenguaje no está diseñado de esa manera.

La función printf es una función ordinaria que acepta argumentos variables. Los argumentos de variables requieren algún tipo de esquema de identificación de tipo de tiempo de ejecución, pero en el lenguaje C, los valores no llevan ninguna información de tipo de tiempo de ejecución. (Por supuesto, los programadores C pueden crear esquemas de tipeo en tiempo de ejecución usando estructuras o trucos de manipulación de bits, pero estos no están integrados en el lenguaje).

Cuando desarrollamos una función como esta:

void foo(int a, int b, ...);

podemos pasar "cualquier" número de argumentos adicionales después del segundo, y depende de nosotros determinar cuántos hay y cuáles son sus tipos utilizando algún tipo de protocolo que esté fuera del mecanismo de paso de funciones.

Por ejemplo, si llamamos a esta función así:

foo(1, 2, 3.0); foo(1, 2, "abc");

no hay forma de que el destinatario pueda distinguir los casos. Solo hay algunos bits en un área de paso de parámetros, y no tenemos idea de si representan un puntero a datos de caracteres o un número de coma flotante.

Las posibilidades de comunicar este tipo de información son numerosas. Por ejemplo, en POSIX, la familia de funciones exec usa argumentos variables que tienen todo el mismo tipo, char * , y un puntero nulo se usa para indicar el final de la lista:

#include <stdarg.h> void my_exec(char *progname, ...) { va_list variable_args; va_start (variable_args, progname); for (;;) { char *arg = va_arg(variable_args, char *); if (arg == 0) break; /* process arg */ } va_end(variable_args); /*...*/ }

Si la persona que llama olvida pasar un terminador de puntero nulo, el comportamiento será indefinido porque la función seguirá invocando va_arg después de haber consumido todos los argumentos. Nuestra función my_exec se debe llamar así:

my_exec("foo", "bar", "xyzzy", (char *) 0);

La conversión en el 0 es necesaria porque no hay contexto para que se interprete como una constante de puntero nulo: el compilador no tiene idea de que el tipo previsto para ese argumento es un tipo de puntero. Además (void *) 0 no es correcto porque simplemente pasará como el tipo void * y no como char * , aunque es casi seguro que los dos sean compatibles a nivel binario, por lo que funcionará en la práctica. Un error común con ese tipo de función exec es esto:

my_exec("foo", "bar", "xyzzy", NULL);

donde el NULL del compilador se define como 0 sin ningún molde (void *) .

Otro posible esquema es requerir que la persona que llama pase un número que indique cuántos argumentos hay. Por supuesto, ese número podría ser incorrecto.

En el caso de printf , la cadena de formato describe la lista de argumentos. La función lo analiza y extrae los argumentos en consecuencia.

Como se mencionó al principio, algunos compiladores, especialmente el C Compiler de GNU, pueden analizar cadenas de formato en tiempo de compilación y realizar una comprobación de tipo estática con respecto al número y tipos de argumentos.

Sin embargo, tenga en cuenta que una cadena de formato puede ser distinta de un literal, y puede calcularse en tiempo de ejecución, que es impermeable a tales esquemas de verificación de tipos. Ejemplo ficticio:

char *fmt_string = message_lookup(current_language, message_code); /* no type checking from gcc in this case: fmt_string could have four conversion specifiers, or ones not matching the types of arg1, arg2, arg3, without generating any diagnostic. */ snprintf(buffer, sizeof buffer, fmt_string, arg1, arg2, arg3);


La razón por la cual el compilador no puede proporcionar la información necesaria es simplemente porque el compilador no está involucrado aquí. El prototipo de las funciones no especifica los tipos, porque estas funciones tienen tipos de variables. Por lo tanto, los tipos de datos reales no se determinan en tiempo de compilación, sino en tiempo de ejecución. La función luego toma un argumento de la pila, después del otro. Estos valores no tienen ningún tipo de información asociada, por lo que la única forma en que la función sabe cómo interpretar los datos es mediante el uso de la información proporcionada por el que llama, que es la cadena de formato.

Las funciones en sí mismas no saben qué tipos de datos se pasan, ni saben la cantidad de argumentos pasados, por lo que no hay forma de que printf pueda decidir esto por sí mismo.

En C ++ puede usar la sobrecarga del operador, pero este es un mecanismo completamente diferente. Porque aquí el compilador elige la función apropiada en función de los tipos de datos y la función disponible sobrecargada.

Para ilustrar esto, printf , cuando se compila se ve así:

push value1 ... push valueN push format_string call _printf

Y el prototipo de printf es este:

int printf ( const char * format, ... );

Por lo tanto, no se transfiere información de tipo, excepto lo que se proporciona en la cadena de formato.


Porque no hay una forma portátil para que funciones de argumentos variables como scanf y printf conozcan los tipos de argumentos variables, ni siquiera cuántos argumentos se pasan.

Consulte C Preguntas frecuentes: ¿cómo puedo descubrir con cuántos argumentos se llamó realmente a una función?

Esta es la razón por la cual debe haber al menos un argumento fijo para determinar el número, y tal vez los tipos, de los argumentos variables. Y este argumento (el estándar lo llama parmN , vea C11 ( ISO / IEC 9899: 201x ) §7.16 Argumentos de variables ) desempeña esta función especial, y se pasará a la macro va_start . En otras palabras, no puede tener una función con un prototipo como este en el estándar C:

void foo(...);


printf no es una función intrínseca . No es parte del lenguaje C per se. Todo lo que hace el compilador es generar código para llamar a printf , pasando cualquier parámetro. Ahora, dado que C no proporciona reflection como mecanismo para determinar la información de tipo en tiempo de ejecución, el programador debe proporcionar explícitamente la información necesaria.


printf y scanf son funciones de E / S que están diseñadas y definidas de una manera para recibir una cadena de control y una lista de argumentos.

Las funciones no conocen el tipo de parámetro que se le pasa, y el compilador tampoco le puede pasar esta información.


scanf como prototipo int scanf ( const char * format, ... ); dice que las tiendas reciben datos de acuerdo con el formato del parámetro en las ubicaciones señaladas por los argumentos adicionales.

No está relacionado con el compilador, todo se trata de la sintaxis definida para scanf El formato de parámetro es necesario para que scanf sepa el tamaño que se debe reservar para ingresar los datos.