Geralmente em C, temos que informar ao computador o tipo de dados na declaração da variável. Por exemplo, no programa a seguir, quero imprimir a soma de dois números de ponto flutuante X e Y.
#include<stdio.h>
main()
{
float X=5.2;
float Y=5.1;
float Z;
Z=Y+X;
printf("%f",Z);
}
Eu tive que dizer ao compilador o tipo de variável X.
- O compilador não pode determinar o tipo
X
sozinho?
Sim, é possível se eu fizer isso:
#define X 5.2
Agora posso escrever meu programa sem informar ao compilador o tipo de X
:
#include<stdio.h>
#define X 5.2
main()
{
float Y=5.1;
float Z;
Z=Y+X;
printf("%f",Z);
}
Portanto, vemos que a linguagem C possui algum tipo de recurso, usando o qual ele pode determinar o tipo de dados por conta própria. No meu caso, determinou que X
é do tipo float.
- Por que precisamos mencionar o tipo de dados, quando declaramos algo em main ()? Por que o compilador não pode determinar o tipo de dados de uma variável por conta própria,
main()
como faz em#define
.
c
variables
data-types
io
declarations
user106313
fonte
fonte
5.2
é adouble
, portanto, o primeiro programa arredonda os literais duplos parafloat
precisão, depois os adiciona como flutuadores, enquanto o segundo arredonda a representação dupla de 5,1 paradouble
e o adiciona aodouble
valor 5.2 usandodouble
adição, depois arredonda o resultado desse cálculo parafloat
precisão . Como o arredondamento ocorre em locais diferentes, o resultado pode ser diferente. Este é apenas um exemplo para os tipos de variáveis que afetam o comportamento de um programa idêntico.#define X 5.2
,X
não é uma variável, mas uma constante, portanto é literalmente substituído por pré-processador por5.2
qualquer lugar que você mencionouX
. Você não pode reatribuirX
.auto
realmente faz o que você deseja). Por outro lado, se você acha que sabe o que seu código está fazendo e, na verdade, digitou outra coisa, a digitação estática assim detectará um erro mais cedo, antes de se tornar um grande problema. Cada idioma atinge um equilíbrio: digitação estática, dedução de tipo, digitação dinâmica. Para algumas tarefas, a digitação extra vale a pena. Para outros, é um desperdício.Respostas:
Você está comparando declarações de variáveis com
#define
s, o que está incorreto. Com a#define
, você cria um mapeamento entre um identificador e um trecho de código-fonte. O pré-processador C substituirá literalmente quaisquer ocorrências desse identificador pelo snippet fornecido. Escrevendoacaba sendo a mesma coisa para o compilador que a escrita
Pense nisso como copiar e colar automaticamente.
Além disso, variáveis normais podem ser reatribuídas, enquanto uma macro criada com
#define
não pode (embora você possa recuperá-#define
lo). A expressãoFOO = 7
seria um erro do compilador, pois não podemos atribuir a "rvalues":40 + 2 = 7
é ilegal.Então, por que precisamos de tipos? Aparentemente, algumas linguagens se livram dos tipos, isso é especialmente comum nas linguagens de script. No entanto, eles geralmente têm algo chamado "digitação dinâmica", onde variáveis não têm tipos fixos, mas valores têm. Embora isso seja muito mais flexível, também é menos eficiente. C gosta de desempenho, por isso tem um conceito muito simples e eficiente de variáveis:
Há uma extensão de memória chamada de "pilha". Cada variável local corresponde a uma área na pilha. Agora, a pergunta é quantos bytes de comprimento essa área precisa ter? Em C, cada tipo tem um tamanho bem definido que você pode consultar
sizeof(type)
. O compilador precisa conhecer o tipo de cada variável para que possa reservar a quantidade correta de espaço na pilha.Por que as constantes criadas com não
#define
precisam de uma anotação de tipo? Eles não são armazenados na pilha. Em vez disso,#define
cria trechos reutilizáveis de código-fonte de uma maneira um pouco mais sustentável do que copiar e colar. Literais no código-fonte como"foo"
ou42.87
são armazenados pelo compilador em linha como instruções especiais ou em uma seção de dados separada do binário resultante.No entanto, literais têm tipos. Um literal de cadeia é a
char *
.42
é umint
mas também pode ser usado para tipos mais curtos (restringindo a conversão).42.8
seria umdouble
. Se você tem um literal e deseja que ele tenha um tipo diferente (por exemplo, para fazer42.8
umfloat
, ou42
umunsigned long int
), pode usar sufixos - uma letra após o literal que altera a maneira como o compilador trata esse literal. No nosso caso, podemos dizer42.8f
ou42ul
.Alguns idiomas têm digitação estática como em C, mas as anotações de tipo são opcionais. Exemplos são ML, Haskell, Scala, C #, C ++ 11 e Go. Como isso funciona? Magia? Não, isso é chamado de "inferência de tipo". Em C # e Go, o compilador examina o lado direito de uma atribuição e deduz o tipo disso. Isso é bastante simples se o lado direito for um literal como
42ul
. Então é óbvio qual deve ser o tipo da variável. Outras linguagens também possuem algoritmos mais complexos que levam em consideração como uma variável é usada. Por exemplo, se você fazx/2
,x
não pode ser uma string, mas deve ter algum tipo numérico.fonte
#define
, estamos tendo uma constante que é diretamente convertida em código binário - por mais longo que seja - e é armazenada na memória como está.#define X 5.2
?printf
você invocou um comportamento indefinido. Na minha máquina, esse trecho imprime um valor diferente a cada vez; no Ideone, ele falha após a impressão zero.X*Y
é válido seX
eY
for ponteiro, mas tudo bem se forint
s;*X
não é válido seX
for umint
, mas tudo bem se for um ponteiro.X no segundo exemplo nunca é um flutuador. É chamado de macro, substitui o valor da macro definido 'X' na fonte pelo valor. Um artigo legível sobre #define está aqui .
No caso do código fornecido, antes da compilação, o pré-processador altera o código
para
e é isso que é compilado.
Isso significa que você também pode substituir esses 'valores' por códigos como
ou mesmo
fonte
#define FOO(...) { __VA_ARGS__ }
.A resposta curta é C precisa de tipos devido ao histórico / que representa o hardware.
História: O C foi desenvolvido no início da década de 1970 e destinado como uma linguagem para programação de sistemas. O código é idealmente rápido e faz o melhor uso dos recursos do hardware.
Tipos de inferência no tempo de compilação seriam possíveis, mas os tempos de compilação já lentos teriam aumentado (consulte o desenho animado de 'compilação' do XKCD. Isso costumava se aplicar ao 'olá mundo' por pelo menos 10 anos após a publicação de C ). Inferir tipos em tempo de execução não seria adequado aos objetivos da programação de sistemas. A dedução do tempo de execução requer uma biblioteca de tempo de execução adicional. C veio muito antes do primeiro PC. Que tinha 256 RAM. Não Gigabytes ou Megabytes, mas Kilobytes.
No seu exemplo, se você omitir os tipos
Então o compilador poderia ter felizmente descoberto que X e Y são flutuadores e transformaram Z no mesmo. De fato, um compilador moderno também descobriria que X & Y não são necessários e apenas configuraria Z como 10.3.
Suponha que o cálculo esteja incorporado dentro de uma função. O gravador de funções pode querer usar seu conhecimento do hardware ou o problema que está sendo resolvido.
Um duplo seria mais apropriado do que um carro alegórico? Leva mais memória e é mais lento, mas a precisão do resultado seria maior.
Talvez o valor de retorno da função possa ser int (ou longo) porque os decimais não são importantes, embora a conversão de float para int não seja sem custo.
O valor de retorno também pode ser dobrado, garantindo que float + float não transborde.
Todas essas perguntas parecem inúteis para a grande maioria do código escrito hoje, mas eram vitais quando o C foi produzido.
fonte
C não possui inferência de tipo (é assim que é chamado quando um compilador adivinha o tipo de uma variável para você) porque é antigo. Foi desenvolvido no início dos anos 70
Muitas linguagens mais recentes possuem sistemas que permitem o uso de variáveis sem especificar seu tipo (ruby, javascript, python etc.)
fonte
true
éboolean
), não variáveis (por exemplo,var x
podem conter valor de qualquer tipo). Além disso, a inferência de tipo para casos simples como os da questão provavelmente já foi conhecida uma década antes do lançamento do C.implicit none
nesse caso, você deve declarar um tipo.