Mostrando postagens com marcador tipos. Mostrar todas as postagens
Mostrando postagens com marcador tipos. Mostrar todas as postagens

segunda-feira, 30 de novembro de 2009

Juntos somos mais eficientes! (vetores)

Até agora vimos somente os tipos básicos do C (aqui), e fizemos muita coisa com eles. Mas chega um momento em que eles não são eficientes para resolver alguns problemas. Quer um exemplo?
Imagine um programa que receba 100 valores inteiros digitados por um usuário. Imaginou? Se não, veja como ele seria usando o que vimos até agora.


//PROGRAMA QUE RECEBE 100 NÚMEROS INTEIROS
#include <stdio.h>
#include <stdlib.h>
 
int main(){
 int val001, val002, val003, val004, val005, ... , val100;
 
 printf("Digite 100 numeros inteiros:\n");
 scanf("%d",&val001);
 scanf("%d",&val002);
 scanf("%d",&val003);
 scanf("%d",&val004);
 scanf("%d",&val005);
    .
    .
    .
 scanf("%d",&val100);
 system("pause");
}


Percebeu a quantidade de linhas que esse programa teria? Sabia que é possível fazer este mesmo programa com 16 linhas (até menos) e de forma muito mais inteligente?
Dê uma olhada nesse exemplo.


//PROGRAMA QUE RECEBE 100 NÚMEROS INTEIROS
#include <stdio.h>
#include <stdlib.h>
 
int main(){
 int vetor[100], i;
 
 printf("Digite 100 numeros inteiros:\n");
 for(i=0; i < 100; i++){
    printf ("Valor %d: ",i+1);
    scanf("%d",&vetor[i]);
 }
 
 system("pause");
}


Este código acima está completo e funciona perfeitamente (não faz muita coisa, é verdade, mas funciona ;). Vamos ver o 'segredo' por trás disto.

Observe a linha 6. Nela é declarado um vetor de 100 inteiros. Vetor???

Um grupo de iguais.

Um vetor é um tipo estruturado homogêneo.(???)
Estruturado significa que ele é uma composição de tipos básicos, ou seja, tipos básicos organizados em um grupo/agrupamento. E homogêneo, significa que esses tipos básicos são iguais; no nosso exemplo, são 100 inteiros (é possível criar agrupamentos heterogêneos, como veremos em outro momento).
Não entendeu ainda? Quer que eu desenhe?



A figura acima representa um vetor de 4 inteiros armazenado na memória. Observe que o armazenamento é consecutivo, ou seja, cada valor inteiro está armazenado num endereço da memória, e estes endereços estão em sequência. Se tivéssemos declarado 4 variáveis inteiras não poderíamos garantir que elas ficariam armazenadas em sequência na memória.
Por estarem armazenados em sequência na memória, podemos acessar o conteúdo de cada endereço também em sequência. Esse é o 'truque' do vetor.
Mas como é feito esse acesso?

Leia o índice!

Com certeza você já teve contato com algum livro (se não, aconselho que o faça rsrs). Podemos generalizar um livro como uma sequência de páginas (escritas ou não) agrupadas. Todas as páginas tem o mesmo tamanho (não me lembro de ter visto algum em que isto não fosse verdadeiro).
Quando queremos encontrar alguma informação específica num livro temos duas opções: ou folheamos o livro, página por página, analisando uma a uma; ou vamos ao seu índice e localizamos o número exato da página onde está a informação que procuramos. Qual delas você considera mais eficiente?
Em termos de comparação, tirando as muitas diferenças, um vetor se assemelha muito a um livro. São posições de memória do mesmo tamanho agrupadas contendo algum valor válido (ou não). Já que são tão parecidos, por que não usar um índice também com um vetor? Facilitaria muito, não concorda? Pois é exatamente assim que funciona.
Vamos a outro desenho.



Para acessar o conteúdo da 1a posição do vetor da figura acima, é só se referir ao nome do vetor e ao índice 0 (zero). Os vetores em C SEMPRE começam com o índice 0 (zero).
Digamos que o nome do vetor da figura acima seja vet. Para exibir o conteúdo da sua 1a posiçao, usamos o comando:

 printf("%d", vet[0]);

Seria impresso o valor 67.

Fácil, não? Só devemos prestar atenção a 2 pontos importantíssimos:

1o. O primeiro índice de um vetor em C SEMPRE começa em 0 (zero)
2o. O último índice de um vetor em C é SEMPRE o tamanho do vetor menos 1.

Isso não significa que não se possa acessar um elemento fora do vetor.
Se usássemos no vetor dos desenhos acima o comando:

 printf("%d",vet[4]);

Algo seria impresso na tela. Não temos como saber o que seria, mas que funcionaria, funcionaria. Por quê?

Um nome de um vetor seguido de um índice i significa que queremos acessar o endereço que está i posições depois do endereço inicial do vetor.
Se um vetor de 4 inteiros está armazenado na memória, por exemplo, a partir do endereço 44 e o tamanho de um inteiro é 4 bytes; o elemento de índice 3 deste vetor estará no endereço 56.

 endereço inicial do vetor(44) + 3 * (tamanho do tipo do vetor (4)

Se tentarmos acessar o índice 4, que sabemos 'não existir', na verdade estaremos acessando o endereço 60 da memória, e 'interpretando' o seu conteúdo como um valor inteiro (este conteúdo pode ser qualquer coisa que esteja na memória, possivelmente deixada por algum outro programa, frequentemente no referimos a ele como 'lixo').
Algumas linguagens de programação não permitem esse tipo de acesso, mas o C não faz restrições. É sua responsabilidade como programador evitar esse tipo de coisa.

Agora que conhecemos o tipo vetor, ficou fácil entender o código do nosso exemplo. Certo?

domingo, 18 de outubro de 2009

A linguagem C. Tipos básicos.

Depois da nossa (necessária) introdução sobre o funcionamento do computador, vamos ao que realmente interessa.

A linguagem C.
Não vou falar aqui sobre a história da linguagem C, que você pode ver aqui; vamos direto ao lado prático da 'coisa'.

Nos posts anteriores falamos bastante sobre PROCESSADOR e MEMÓRIA. Talvez você tenha achado isto muito chato (eu também achava ;), mas garanto que depois de ler este post, isso vai mudar um pouco.

Um programa de computador é uma série de instruções que diz ao PROCESSADOR o que ele deve fazer (cálculos, pesquisas, etc.). Para fazer o que é ordenado ele usa a MEMÓRIA durante todo o tempo. É lá que ficam os dados que ele tem que processar e as instruções que ele tem que executar.
Até aqui nos referimos à MEMÓRIA como um 'quadro'. Isto é útil para entender o conceito de dados 'rápidos'. Entretanto, precisamos ampliar este conceito para algo mais próximo do mundo real.
Falamos que poderíamos escrever em qualquer lugar do 'quadro', mas não deixamos claro como o PROCESSADOR encontra a posição onde escrever.
Para explicar como, vou usar a analogia de uma folha de papel quadriculado. Seria como se existissem várias linhas verticais e horizontais paralelas, dividindo o nosso quadro em vários 'quadrinhos' (como uma planilha do Excel). Esses 'quadrinhos' são então endereçados (recebem uma identificação); se pensarmos em termos de planilha, cada um desses 'quadrinhos' está em uma linha e coluna específica; o que torna cada um deles único e inconfundível.
Qual o tamanho de cada 'quadrinho' destes? Isto depende do que vai ser escrito nele.
Lembra do conceito de byte? Pois bem, cada 'quadrinho' tem exatamente um byte, o que torna possível armazenar cada símbolo num único 'quadrinho'.
Pensando assim, a palavra CASA ocupará 4 'quadrinhos' (na verdade 5, mas isto será visto mais tarde), e o número 1000 ocupará também 4 'quadrinhos'. (Uma palavra é representada pelos seus símbolos na tabela ASCII e um número é representado pelo seu equivalente binário)


Hoje com MEMÓRIAS com a capacidade superiores a 1.000.000.000 bytes custando poucos R$, não haveria problema nenhum com esta lógica. Só que quando os computadores foram criados, as MEMÓRIAS eram bem menores e muito mais caras, e usar 4 'quadrinhos' para armazenar o número 1000 era desperdício. Por quê?

Lembra que dissemos que qualquer número poderia ser representado de forma binária? Pois bem, o número decimal 1000, corresponde ao número binário 1111101000, que tem 10 bits, 2 a mais que os 8 bits de 1 byte. Sendo assim se usarmos 2 'quadrinhos' podemos armazenar o número 1000 em formato binário, o que significa uma economia de 2 'quadrinhos'; no caso, 2 bytes.


E como o PROCESSADOR saberá quantos 'quadrinhos' usar para fazer o que é ordenado?
Como sabemos o COMPUTADOR/PROCESSADOR só faz o que lhe é ordenado e da forma que lhe é ordenado. Então só precisamos 'dizer' a ele quantos 'quadrinhos' queremos que ele use.
É aí que entra um conceito fundamental da linguagem C: os TIPOS.

A linguagem C define 4 tipos básicos: char, int, float e void.

O char ocupa exatamente 1 byte, e é usado para representar símbolos (letras, acentos, caracteres de pontuação), ou qualquer outra coisa que possa ser representada usando 1 byte; pequenos números, por exemplo (até 255 'cabe' em 1 byte).

O int é usado para armazenar números inteiros, normalmente usa 2 bytes (alguns computadores podem usar 4 bytes).

O float é usado para armazenar números com casas decimais, e para isto usa, na maioria dos computadores, pelo menos 4 bytes.

O tipo void é um tipo especial, e representa NADA. Você não leu errado. Representa NADA; na verdade, um nada que pode se transformar em qualquer coisa (meus colegas de faculdade: notaram alguma semelhança com o conjunto vazio das aulas de matemática discreta rsrsrs).

Até aqui tranquilo, certo?
Então me diga: Quantos bytes são necessários para representar o número 99999?
Depois de fazer algumas contas (eu não, a calculadora do windows ;), convertendo o número decimal 99999 para o seu equivalente binário, temos 11000011010011111, que tem 17 bits, 2 bytes mais 1 bit. Se o computador em que estamos trabalhando usar 2 bytes para armazenar o tipo int, o número 99999 decimal não vai 'caber'. E agora?
Para isto o C (vou chamar a linguagem C assim, já que agora estamos ficando íntimos ;) utiliza um recurso chamado de modificadores de tipo.

Os modificadores de tipo usados em C são 4 (de novo!): long, short, signed e unsigned.

O modificador long dobra a quantidade de bytes usados para armazenar o tipo int, se forem 2 bytes passam a ser 4 bytes, ou seja, 32 bits (será que agora 'cabe'? ;).

Por hora,vamos falar somente do long. Os outros serão vistos ao longo da nossa caminhada.


Uma última coisa para encerrar este post (se é última coisa, claro que é pra encerrar rsrs!).
Lembra que falamos que se houver alguma coisa 'escrita' num dos nossos 'quadrinhos' ela seria perdida pra dar lugar a nova? Podemos dizer então, que o conteúdo deste 'quadrinho' pode mudar, ou melhor dizendo, variar. Por causa disto, quadrinho recebe o nome de 'VARIÁVEL'.
Se quisermos que o conteúdo do nosso 'quadrinho' não mude nunca, mandamos que o PROCESSADOR não deixe que ele seja alterado; ou seja, que trate este quadrinho como 'CONSTANTE'.

Até agora vimos o que 'dizer', mas ainda não vimos como 'dizer'.
Mas isto, só no próximo post.