Por lo general, en C, tenemos que decirle a la computadora el tipo de datos en la declaración de variables. Por ejemplo, en el siguiente programa, quiero imprimir la suma de dos números de coma flotante X e Y.
#include<stdio.h>
main()
{
float X=5.2;
float Y=5.1;
float Z;
Z=Y+X;
printf("%f",Z);
}
Tuve que decirle al compilador el tipo de variable X.
- ¿No puede el compilador determinar el tipo de
X
por sí mismo?
Sí, puede hacerlo si hago esto:
#define X 5.2
Ahora puedo escribir mi programa sin decirle al compilador el tipo de X
como:
#include<stdio.h>
#define X 5.2
main()
{
float Y=5.1;
float Z;
Z=Y+X;
printf("%f",Z);
}
Entonces, vemos que el lenguaje C tiene algún tipo de característica, mediante la cual puede determinar el tipo de datos por sí mismo. En mi caso se determinó que X
es de tipo float.
- ¿Por qué tenemos que mencionar el tipo de datos cuando declaramos algo en main ()? ¿Por qué el compilador no puede determinar el tipo de datos de una variable por sí solo
main()
como lo hace en#define
?
c
variables
data-types
io
declarations
usuario106313
fuente
fuente
5.2
es undouble
, entonces el primer programa redondea los literales dobles afloat
precisión, luego los agrega como flotantes, mientras que el segundo redondea la representación doble de 5.1double
y lo agrega aldouble
valor 5.2 usando ladouble
suma, luego redondea el resultado de ese cálculo afloat
precisión . Debido a que el redondeo ocurre en diferentes lugares, el resultado puede disminuir. Este es solo un ejemplo para los tipos de variables que afectan el comportamiento de un programa idéntico.#define X 5.2
,X
no es una variable, sino una constante, por lo que literalmente se reemplaza por preprocesador con5.2
cualquier lugar que haya mencionadoX
. No puedes reasignarX
.auto
realidad, C ++ hace lo que quiere). Por otro lado, si crees que sabes lo que está haciendo tu código y escribiste algo más, la escritura estática como esta detectará un error antes, antes de que se convierta en un gran problema. Cada idioma logra un equilibrio: escritura estática, inferencia de tipos, escritura dinámica. Para algunas tareas, la escritura adicional realmente vale la pena. Para otros, es un desperdicio.Respuestas:
Está comparando declaraciones de variables con
#define
s, lo cual es incorrecto. Con a#define
, crea una asignación entre un identificador y un fragmento de código fuente. El preprocesador C sustituirá literalmente cualquier aparición de ese identificador con el fragmento proporcionado. Escrituratermina siendo lo mismo para el compilador que escribir
Piense en ello como copiar y pegar automatizado.
Además, las variables normales se pueden reasignar, mientras que una macro creada con
#define
no se puede (aunque se puede reasignar#define
). La expresiónFOO = 7
sería un error del compilador, ya que no podemos asignar "valores":40 + 2 = 7
es ilegal.Entonces, ¿por qué necesitamos tipos? Aparentemente, algunos idiomas eliminan los tipos, esto es especialmente común en los lenguajes de secuencias de comandos. Sin embargo, generalmente tienen algo llamado "tipeo dinámico" donde las variables no tienen tipos fijos, pero los valores sí. Si bien esto es mucho más flexible, también es menos eficiente. A C le gusta el rendimiento, por lo que tiene un concepto de variables muy simple y eficiente:
Hay un tramo de memoria llamado "pila". Cada variable local corresponde a un área en la pila. Ahora la pregunta es ¿cuántos bytes tiene esta área? En C, cada tipo tiene un tamaño bien definido a través del cual puede consultar
sizeof(type)
. El compilador necesita saber el tipo de cada variable para poder reservar la cantidad correcta de espacio en la pila.¿Por qué las constantes creadas con
#define
necesitan una anotación de tipo? No se almacenan en la pila. En cambio,#define
crea fragmentos reutilizables de código fuente de una manera ligeramente más fácil de mantener que copiar y pegar. Los literales en el código fuente, como el compilador"foo"
o los42.87
almacena, ya sea en línea como instrucciones especiales o en una sección de datos separada del binario resultante.Sin embargo, los literales tienen tipos. Un literal de cadena es a
char *
.42
es unint
pero también se puede usar para tipos más cortos (conversión de reducción).42.8
sería adouble
. Si tiene un literal y desea que tenga un tipo diferente (por ejemplo, para hacer42.8
unfloat
, o42
ununsigned long int
), puede usar sufijos, una letra después del literal que cambia la forma en que el compilador trata ese literal. En nuestro caso, podríamos decir42.8f
o42ul
.Algunos lenguajes tienen escritura estática como en C, pero las anotaciones de tipo son opcionales. Ejemplos son ML, Haskell, Scala, C #, C ++ 11 y Go. ¿Cómo funciona? ¿Magia? No, esto se llama "inferencia de tipos". En C # y Go, el compilador mira el lado derecho de una tarea y deduce el tipo de eso. Esto es bastante sencillo si el lado derecho es un literal como
42ul
. Entonces es obvio cuál debería ser el tipo de variable. Otros lenguajes también tienen algoritmos más complejos que tienen en cuenta cómo se usa una variable. Por ejemplo, si lo hacex/2
,x
no puede ser una cadena, pero debe tener algún tipo numérico.fuente
#define
tenemos una constante que se convierte directamente en código binario, por mucho tiempo que sea, y se almacena en la memoria tal como está.#define X 5.2
?printf
invocaste un comportamiento indefinido. En mi máquina, ese fragmento imprime un valor diferente cada vez, en Ideone se bloquea después de imprimir cero.X*Y
no es válido siX
yY
son punteros, pero está bien si sonint
s;*X
no es válido siX
es unint
, pero está bien si es un puntero.X en el segundo ejemplo nunca es un flotador. Se llama macro, reemplaza el valor macro definido 'X' en la fuente con el valor. Un artículo legible sobre #define está aquí .
En el caso del código suministrado, antes de la compilación, el preprocesador cambia el código
a
y eso es lo que se compila.
Eso significa que también puede reemplazar esos 'valores' con código como
o incluso
fuente
#define FOO(...) { __VA_ARGS__ }
.La respuesta corta es que C necesita tipos debido a la historia / que representa el hardware.
Historia: C se desarrolló a principios de la década de 1970 y pretendía ser un lenguaje para la programación de sistemas. El código es idealmente rápido y hace el mejor uso de las capacidades del hardware.
Habría sido posible inferir tipos en el momento de la compilación, pero los tiempos de compilación ya lentos habrían aumentado (consulte la caricatura de 'compilación' de XKCD. Esto solía aplicarse al 'hola mundo' durante al menos 10 años después de la publicación de C ). Inferir tipos en tiempo de ejecución no habría encajado con los objetivos de la programación de sistemas. La inferencia de tiempo de ejecución requiere una biblioteca de tiempo de ejecución adicional. C llegó mucho antes de la primera PC. Que tenía 256 RAM. No Gigabytes o Megabytes sino Kilobytes.
En su ejemplo, si omite los tipos
Entonces, el compilador pudo haber deducido felizmente que X e Y son flotantes e hizo Z igual. De hecho, un compilador moderno también resolvería que X e Y no son necesarios y solo establecería Z en 10.3.
Suponga que el cálculo está incrustado dentro de una función. El escritor de funciones podría querer usar su conocimiento del hardware o el problema que se está resolviendo.
¿Sería un doble más apropiado que un flotador? Toma más memoria y es más lento, pero la precisión del resultado sería mayor.
Quizás el valor de retorno de la función podría ser int (o largo) porque los decimales no eran importantes, aunque la conversión de float a int no está exenta de costo.
El valor de retorno también se puede hacer doble garantizando que float + float no se desborde.
Todas estas preguntas parecen inútiles para la gran mayoría del código escrito hoy, pero fueron vitales cuando se produjo C.
fuente
C no tiene inferencia de tipos (así se llama cuando un compilador adivina el tipo de una variable para usted) porque es antigua. Fue desarrollado a principios de los años 70.
Muchos idiomas más nuevos tienen sistemas que le permiten usar variables sin especificar su tipo (ruby, javascript, python, etc.)
fuente
true
esboolean
), no variables (por ejemplo,var x
pueden contener valores de cualquier tipo). Además, la inferencia de tipos para casos tan simples como los cuestionados probablemente se haya conocido una década antes de que se lanzara C.implicit none
en cuyo caso debe declarar un tipo.