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

terça-feira, 7 de outubro de 2014

Ponteiros

Como Funcionam os Ponteiros 

Os ints guardam inteiros. Os floats guardam números de ponto flutuante. Os chars guardam caracteres. Ponteiros guardam endereços de memória. Quando você anota o endereço de um colega você está criando um ponteiro. O ponteiro é este seu pedaço de papel. Ele tem anotado um endereço. Qual é o sentido disto? Simples. Quando você anota o endereço de um colega, depois você vai usar este endereço para achá-lo. O C funciona assim. Você anota o endereço de algo numa variável ponteiro para depois usar.

Da mesma maneira, uma agenda, onde são guardados endereços de vários amigos, poderia ser vista como sendo uma matriz de ponteiros no C. 

Um ponteiro também tem tipo. Veja: quando você anota um endereço de um amigo você o trata diferente de quando você anota o endereço de uma firma. Apesar de o endereço dos dois locais ter o mesmo formato (rua, número, bairro, cidade, etc.) eles indicam locais cujos conteúdos são diferentes. Então os dois endereços são ponteiros de tipos diferentes. 

No C quando declaramos ponteiros nós informamos ao compilador para que tipo de variável vamos apontá-lo. Um ponteiro int aponta para um inteiro, isto é, guarda o endereço de um inteiro. 

Declarando e Utilizando Ponteiros 

Para declarar um ponteiro temos a seguinte forma geral:

tipo_do_ponteiro *nome_da_variável; 

É o asterisco (*) que faz o compilador saber que aquela variável não vai guardar um valor mas sim um endereço para aquele tipo especificado. Vamos ver exemplos de declarações: 

int *pt;
char *temp,*pt2; 

O primeiro exemplo declara um ponteiro para um inteiro. O segundo declara dois ponteiros para caracteres. Eles ainda não foram inicializados (como toda variável do C que é apenas declarada). Isto significa que eles apontam para um lugar indefinido. Este lugar pode estar, por exemplo, na porção da memória reservada ao sistema operacional do computador. 

Usar o ponteiro nestas circunstâncias pode levar a um travamento do micro, ou a algo pior. O ponteiro deve ser inicializado (apontado para algum lugar conhecido) antes de ser usado! Isto é de suma importância! 

Para atribuir um valor a um ponteiro recém-criado poderíamos igualá-lo a um valor de memória. Mas, como saber a posição na memória de uma variável do nosso programa? Seria muito difícil saber o endereço de cada variável que usamos, mesmo porque estes endereços são determinados pelo compilador na hora da compilação e realocados na execução. Podemos então deixar que o compilador faça este trabalho por nós. Para saber o endereço de uma variável basta usar o operador &. Veja o exemplo: 

 int count=10; 
 int *pt; 
 pt=&count; 

Criamos um inteiro count com o valor 10 e um apontador para um inteiro pt. A expressão &count nos dá o endereço de count, o qual armazenamos em pt. Simples, não é? 

Repare que não alteramos o valor de count, que continua valendo 10. 

Como nós colocamos um endereço em pt, ele está agora "liberado" para ser usado. Podemos, por exemplo, alterar o valor de count usando pt. Para tanto vamos usar o operador "inverso" do operador &. É o operador *. No exemplo acima, uma vez que fizemos pt=&count a expressão *pt é equivalente ao próprio count. Isto significa que, se quisermos mudar o valor de count para 12, basta fazer *pt=12

- Que tal voltarmos à nossa analogia para ver o que está acontecendo. 

Digamos que exista uma firma. Ela é como uma variável que já foi declarada. Você tem um papel em branco onde vai anotar o endereço da firma. O papel é um ponteiro do tipo firma. Você então liga para a firma e pede o seu endereço, o qual você vai anotar no papel. Isto é equivalente, no C, a associar o papel à firma com o operador &. Ou seja, o operador &aplicado à firma é equivalente a você ligar para a mesma e pedir o endereço. Uma vez de posse do endereço no papel você poderia, por exemplo, fazer uma visita à firma. No C você faz uma visita à firma aplicando o operador * ao papel. Uma vez dentro da firma você pode copiar seu conteúdo ou modificá-lo. 

 Uma observação importante: apesar do símbolo ser o mesmo, o operador * (multiplicação) não é o mesmo operador que o * (referência de ponteiros). Para começar o primeiro é binário, e o segundo é unário pré-fixado.

 Aqui vão dois exemplos de usos simples de ponteiros:

#include <stdio.h> 
int main () 
 int num,valor; 
 int *p; 
 num=55; 
 p=&num; 
/* Pega o endereco de num */ 
 valor=*p; 
 /* Valor e igualado a num de uma maneira indireta */ 
 printf ("\n\n%d\n",valor); 
 printf ("Endereco para onde o ponteiro aponta: %p\n",p); 
 printf ("Valor da variavel apontada: %d\n",*p); 
 return(0); 
}

e:

#include <stdio.h> 
int main () 
 int num,*p; 
 num=55; 
 p=&num; /* Pega o endereco de num */ 
 printf ("\nValor inicial: %d\n",num); 
 *p=100; /* Muda o valor de num de uma maneira indireta */ 
 printf ("\nValor final: %d\n",num); 
 return(0); 
}

Nos exemplos acima vemos um primeiro exemplo do funcionamento dos ponteiros. No primeiro exemplo, o código %p usado na função printf() indica à função que ela deve imprimir um endereço. 

Podemos fazer algumas operações aritméticas com ponteiros. A primeira, e mais simples, é igualar dois ponteiros. Se temos dois ponteiros p1 e p2 podemos igualá-los fazendo p1=p2. Repare que estamos fazendo com que p1 aponte para o mesmo lugar que p2. Se quisermos que a variável apontada por p1 tenha o mesmo conteúdo da variável apontada por 41 p2 devemos fazer *p1=*p2. Basicamente, depois que se aprende a usar os dois operadores (& e *) fica fácil entender operações com ponteiros.
As próximas operações, também muito usadas, são o incremento e o decremento. Quando incrementamos um ponteiro ele passa a apontar para o próximo valor do mesmo tipo para o qual o ponteiro aponta. Isto é, se temos um ponteiro para um inteiro e o incrementamos ele passa a apontar para o próximo inteiro. Esta é mais uma razão pela qual o compilador precisa saber o tipo de um ponteiro: se você incrementa um ponteiro char* ele anda 1 byte na memória e se você incrementa um ponteiro double* ele anda 8 bytes na memória. O decremento funciona semelhantemente. Supondo que p é um ponteiro, as operações são escritas como: 

p++; 
p--; 

Mais uma vez insisto. Estamos falando de operações com ponteiros e não de operações com o conteúdo das variáveis para as quais eles apontam. Por exemplo, para incrementar o conteúdo da variável apontada pelo ponteiro p, faz-se: 

(*p)++; 

Outras operações aritméticas úteis são a soma e subtração de inteiros com ponteiros. Vamos supor que você queira incrementar um ponteiro de 15. Basta fazer: 

 p=p+15; ou p+=15; 

E se você quiser usar o conteúdo do ponteiro 15 posições adiante: 

 *(p+15); 

A subtração funciona da mesma maneira. Uma outra operação, às vezes útil, é a comparação entre dois ponteiros. Mas que informação recebemos quando comparamos dois ponteiros? Bem, em primeiro lugar, podemos saber se dois ponteiros são iguais ou diferentes (== e !=). No caso de operações do tipo >, <, >= e <= estamos comparando qual ponteiro aponta para uma posição mais alta na memória. Então uma comparação entre ponteiros pode nos dizer qual dos dois está "mais adiante" na memória. A comparação entre dois ponteiros se escreve como a comparação entre outras duas variáveis quaisquer: 

 p1>p2 

Há entretanto operações que você não pode efetuar num ponteiro. Você não pode dividir ou multiplicar ponteiros, adicionar dois ponteiros, adicionar ou subtrair floats ou doubles de ponteiros. 

domingo, 5 de outubro de 2014

Vetores (array)

Em diversas situações os tipos básicos de dados (int, float, char, etc.) não são suficientes para representar a informação que se deseja armazenar. Exemplo, uma palavra: “AULA”. Existe a possibilidade de construção de novos tipos de dados a partir da composição (ou abstração) de tipos de dados primitivos. Esses novos tipos têm um formato denominado ESTRUTURA DE DADOS, que define como os tipos primitivos estão organizados. 


Exemplo:
Definir a média das notas de 50 alunos de uma turma. 


#define N 50  //constante simbolica
int main(){
/*le as notas de N alunos de uma turma
 e calcula a media da turma*/

int i;
float nota, media, soma = 0;

for (i=0; i<=N; i++){
printf("Digite uma nota: ");
scanf("%f",&nota);
soma = soma + nota;
}
media = soma/N;
printf("Media = %0.2f", media);
return 0;
}


Mas se além da média quisesse fazer um programa em C para imprimir as notas lidas juntamente com a média da turma .Quando uma determinada Estrutura de Dados for composta de variáveis com o mesmo tipoprimitivo, temos um conjunto homogêneo de dados. Essas variáveis são chamadas de variáveis compostas homogêneas. As variáveis compostas homogêneas unidimensionais são utilizadas para representar arranjos unidimensionais de elementos de um mesmo tipo, em outras palavras, são utilizadas para representar vetores.


A sintaxe para declaração de uma variável deste tipo é: tipo_primitivo identificador[qtde_elementos];

Exemplo:
int dados[5]; // vetor com 5 (0 a 4) elementos do tipo int










Sempre começará a contar a primeira posição do zero.
Para fornecer os elementos de cada posição do vetor é declarado da seguinte forma:

Exemplo:
float valores[4]={1.2,3.6,4.5,2.8};






Se fosse "float dados[3]={1.2};" ou seja há menos valores do que o número de elementos do array, os elementos restantes são inicializados automaticamente com o valor zero.

Importante:
Os vetores não são inicializados automaticamente.

Declarar mais elementos do que defino causa erro de sintaxe.

Omitindo o tamanho do vetor ele terá o mesmo número de elementos digitados.

int n[ ] = {1, 2, 3, 4, 5};
ou seja 5 elementos,5 posições,índices do vetor variam de 0 a 4.

Cada um dos elementos de um vetor é referenciado individualmente por meio de um número inteiro entre colchetes após o nome do vetor.





X = valores[1]; (8.9)
Y = valores[3]; (4.5)
//Ambas as formas atribuem um valor ao elemento 0 (zero) do vetor valores:
i=0;
valores[0] = 6.7;
/*ou
valores[i]=6.7;
O programa a seguir inicializa os dez elementos de um array s com os valores: 2, 4, 6, ..., 20 e o imprime.*/
#include <stdio.h>
#define TAMANHO 10
int main()
{
int s[TAMANHO], j;
for (j=0; j<= TAMANHO - 1; j++)
{
s[j] = 2 + 2*j;
printf("Elemento %d tem valor de %d \n”,j,s[j]);
}
return 0;
}


Com o intuito de contribuir com a disseminação do conhecimento compartilho e  recomendo que assistam a vídeo aula abaixo:



"O mérito do homem não reside no conhecimento que tem, mas no esforço que despendeu para alcançá-lo."
Gotthold Ephraim Lessing.