AULA 15 - Microcontroladores - Técnico: mudanças entre as edições

De IFSC
Ir para navegação Ir para pesquisar
imported>Fargoud
imported>Fargoud
Linha 396: Linha 396:
==Exercícios==
==Exercícios==
#"Os ponteiros permitem a passagem de valores por referência para uma função". Demonstre esta propriedade através de um programa.
#"Os ponteiros permitem a passagem de valores por referência para uma função". Demonstre esta propriedade através de um programa.
# Faça um programa que calcule a média aritmética de um número arbitrário de notas de
# Faça um programa que calcule a média aritmética de um número arbitrário de notas de provas, usando matrizes.
#Repita o programa acima, agora utilizando ponteiros.
#Repita o programa acima, agora utilizando ponteiros.



Edição das 14h52min de 8 de junho de 2017

PONTEIROS

Uma das características mas poderosas oferecidas pela linguagem C é o uso de ponteiros.

Mas também é visto como um do tópicos mais complexos da linguagem.

Isto ocorre principalmente porque os conceitos embutidos em ponteiros podem ser novos para muitos programadores (tendo-se em vista que nem todas as linguagens utilizam ponteiros), mas também porque os símbolos usados para notação de ponteiros em C não são tão claros; por exemplo, o mesmo símbolo ("*") é usado para duas diferentes finalidades.

Mas, o que são ponteiros?

Ponteiros proporcionam um modo de acesso a variáveis sem referenciá-las diretamente, através do seu endereço.

Basicamente, um ponteiro é uma representação simbólica de um endereço.

Para declarar uma variável como ponteiro, a sintaxe é:

 tipo  * nome_da_variável;

onde tipo é o tipo de variável para qual o ponteiro vai apontar e nome_da_variável é o nome do ponteiro.

Ou seja, a variável ponteiro nome_da_variável vai conter o endereço de uma outra variável do tipo tipo.

Além disto, quando quisermos definir o ponteiro, utilizaremos o operador "&" para atribuir "o endereço de" uma variável ao ponteiro.

Exemplo 1:

   int  * pont_i ;  //pont_i é um ponteiro para variáveis int
   float * pont_f;  //pont_f é um ponteiro para variáveis float
   int i=5;
     ...
   pont_i = &i;    // coloca o endereço de i em pont_i     

A variável pont_i agora "aponta para i".

Caso precisemos acessar o conteúdo do endereço apontado por pont_i, ou seja, o conteúdo de i, utilizaremos o operador "*", que neste caso significa "valor no endereço de":

*pont_i = 5; //coloca 5 no endereço apontado por pont_i (i=5) 

Os ponteiros podem ser inicializados já na declaração, como qualquer variável:

 int *pi = &i; 
   

Além disto, os ponteiros podem ser utilizados na maioria das expressões válidas em C.

Ponteiros são usados em situações em que a passagem de valores é é difícil ou indesejável.

Algumas razões para o uso de ponteiros são:

  1. fornecem maneiras com as quais as funções podem realmente modificar os argumentos que recebem;
  2. para passar matrizes e strings mais convenientemente de uma função para outra;
  3. para manipular matrizes mais facilmente através da movimentação de ponteiros para elas;
  4. para criar estruturas de dados complexas, como listas encadeadas e árvores binárias, onde uma estrutura de dados deve conter referências sobre outra;
  5. para comunicar informações sobre memória, como na função malloc() que retorna a localização de memória livre através do uso de ponteiro;
  6. a notação de ponteiros compila muito mais rapidamente, tornando o código mais eficiente.

Outra coisa muito importante, é lembrar que podemos passar vários valores como argumentos para uma função, porém apenas sabemos como retornar um único valor (através do comando "return").

Mas como fazer para que uma função altere mais de um valor para a função chamadora?

Visto que não há mecanismos próprios para isto, devemos contar com o uso de ponteiros.

Exemplo 2:

#include <stdio.h>
void main(void)
{     int x=4, y=7;
  int *px, *py;
  printf("x é:   %d,  y é:  %d. \n", x, y);
  px = &x;
  py = &y;
  printf(" O endereço de x é: %u, e o de y é:  %u \n", px, py);
  *px = *px + 10;   // pode-se usar  *px += 10;
  *py = *py + 10;   // e também    (*py ) +=10
  printf(" Agora x é:  %d  e y é:  %d \n", x, y);
  printf(" ... agora px aponta p/ %u, e py p/ %u\n", ++px, ++py);
  printf(" ... que contém os valores: %d, %d \n", *px, *py);
}        

Um operador "&" pode ser aplicado somente a variáveis e elementos de matrizes.

Construções tais com "&(x+1)" e "&(3)" são ilegais.

Ponteiros são sempre inicializados com o valor 0 (NULL) que não é um endereço válido, obrigando portanto, que inicializemos sempre nossos ponteiros antes de utilizá-los.

PONTEIROS E MATRIZES

Existe uma correspondência muito grande entre ponteiros e matrizes.

O nome de uma matriz é equivalente ao endereço do primeiro elemento na matriz.

Como isto é imutável, podemos chamar o nome de uma matriz de "ponteiro constante".

Já um ponteiro comum tem conteúdo variável, pode conter qualquer endereço, sendo porisso chamado de "ponteiro variável".

Pode-se fazer, portanto:

   int imat[5] = {5,7,31,18,22};
   int *ip;
      ...
   ip = imat;          // OU  ip=&imat[0];  

e teríamos, por exemplo, "*(ip+n)" como sendo o valor do n-ésimo elemento da matriz imat.


Isto porque o significado da adição ou subtração de um inteiro com um ponteiro é adicionar ou subtrair o tamanho de memória do tipo da variável (sizeof) para o qual o ponteiro foi declarado.

Em outras palavras, ip não vai ser incrementado de n bytes de memória, mas de 2 bytes * n, pois o tipo int ocupa 2 bytes. A disposição na memória, por exemplo, seria:

Variável Endereço Valor
imat 567 630
imat[0] 630 5
imat[1] 632 7
imat[2] 634 31
imat[3] 636 18
imat[4] 638 22
ip 802 630


então:

Expressão Valor Explicação
ip 630 endereço de imat[0]
*ip 5 valor em 630
ip + 1 632 endereço de imat[1]
*(ip+1) 7 valor em 632
*ip + 1 6 valor em 630 mais 1
imat[0] 5
*(imat) 5
imat[1] 7
*(imat+1) 7


Com strings é a mesma coisa:

   char  *pc="ABC";   

define um ponteiro para a série de caracteres "ABC" (terminada com "\0").


O ponteiro pc tem portanto, como valor, o endereço do primeiro elemento da série, o caractere 'A'.

Pode-se ter, também, matrizes de ponteiros:

   static char *cp[3] = {"XYZ", "QRS", "KLM"};   

por exemplo, declara cp como sendo uma matriz de 3 ponteiros para caracteres e já inicializa-os, fazendo-os apontar para as três sequências de caracteres.

Na memória isto ficaria:

Variável Endereço Valor
constante 731 'X'
constante 732 'Y'
constante 733 'Z'
constante 734 '\0'
constante 735 'Q'
constante 736 'R'
constante 737 'S'
constante 738 '\0'
constante 739 'K'
constante 740 'L'
constante 741 'M'
constante 742 '\0'
cp[0] 900 731
cp[1] 902 735
cp[2] 904 739

ARITMÉTICA DE PONTEIROS

Existem poucas operações que devem ou podem ser efetuadas com o valor dos ponteiros.

Essas operações são a atribuição de valores a outros ponteiros, soma e subtração de inteiros, e comparação de igualdade com outro valor de ponteiro.

Ponteiros podem ser adicionados e subtraídos de outros ponteiros, quando estes ponteiros apontam para elementos diferentes de uma mesma matriz.

Por exemplo:

static int imat[10];
int *ip= &imat[0];
int *iq= &imat[4];
...
distancia = iq - ip; // distancia= 4 e significa 
  //o número de elementos int entre iq e ip

Os operadores unários utilizados em ponteiros são "++" e "--".

Supondo um ponteiro para ponto flutuante dupla precisão p1, com valor atual de 2000.

Após a operação:

p1++;

o conteúdo de p1 será 2008 e não 2001.

Para cada incremento de p1, este apontará para o double seguinte, que na maioria dos computadores tem 8 palavras de comprimento.

O mesmo vale para decréscimos:

p1--;

fará com que p1 tenha o valor 1992.

Pode-se comparar ponteiros, através dos testes relacionais ">=, <=, < e >".

Deve-se, no entanto, tomar cuidado para não comparar ponteiros que apontam para tipos diferentes de variáveis, pois os resultados serão sem sentido.

Não se pode multiplicar, dividir, deslocar os bits, somar ou subtrair floats e double aos ponteiros.

PONTEIROS PARA MATRIZES USANDO FUNÇÕES

Vamos analisar como uma função pode usar ponteiros para acessar elementos de uma matriz cujo endereço é passado para a função como argumento.

Como exemplo, vamos ver a função adcon1(), que adiciona uma constante a todos os elementos de uma matriz.

#include <stdio.h>
#define TAM 5
void adcon1(int *, int, int);
void main(void)
{ static int matriz[TAM] = { 3,6,7,9,11};
  int c=10;
  int j;
  adcon1(matriz, TAM, c);
  for(j=0; j<TAM; j++)
    printf("%d", *(matriz+j));
}
// adcon1() - adiciona constante a cada elemento da matriz
void adcon1(int *ptr,int num,int con)
{ int k;
  for(k=0; k<num; k++)
  { *ptr = *ptr + con; ptr++; }
} 

A saída será:

13 16 17 19 21


Na definição da função adcon1(), a declaração " int *ptr; " é equivalente a " int ptr[]; ".

Em outras palavras, a primeira declaração cria um ponteiro variável, enquanto a segunda, um ponteiro constante.


Exercícios

  1. "Os ponteiros permitem a passagem de valores por referência para uma função". Demonstre esta propriedade através de um programa.
  2. Faça um programa que calcule a média aritmética de um número arbitrário de notas de provas, usando matrizes.
  3. Repita o programa acima, agora utilizando ponteiros.

Exemplos

/**********************************************************/
/***** Programa que procura um caractere em uma "string" **/
/**********************************************************/
#include <stdio.h>
#include <conio.h>
char *procstr(char *, char);
void main(void)
{
  char *ptr;
  char ch, lin[81];
  puts("Digite uma frase: ");
  gets(lin);
  printf("Digite o caractere a ser procurado: ");
  ch = getche();
  ptr = procstr(lin, ch);
  printf("\n A string comeca no endereço %u.\n",lin);
  if(ptr)
  { printf("Primeira ocorrencia do caractere: %u.\n", ptr);
    printf("E a posição: %d", ptr-lin);
  }
  else printf("\n caractere nao existe. \n");
  getche();
}

char *procstr(char *linha,char c)
{ while(*linha != c && *linha != '\0')
  linha++;
  if(*linha != '\0')
     return(linha+ 1);
  else
    return(0);
 }

ARITMÉTICA DE PONTEIROS

Existem poucas operações que devem ou podem ser efetuadas com o valor dos ponteiros. Essas operações são a atribuição de valores a outros ponteiros, soma e subtração de inteiros, e comparação de igualdade com outro valor de ponteiro. Ponteiros podem ser adicionados e subtraídos de outros ponteiros, quando estes ponteiros apontam para elementos diferentes de uma mesma matriz. Por exemplo:

static int imat[10];
   int *ip= &imat[0];
   int *iq= &imat[4];
   ...
   distancia = iq - ip;     // distancia= 4 e significa o número de elementos int entre iq e ip    
  

Os operadores unários utilizados em ponteiros são "++" e "--". Supondo um ponteiro para ponto flutuante p1, com valor atual de 2000. Após a operação:

p1++;

o conteúdo de p1 será 2008 e não 2001. Para cada incremento de p1, este apontará para o double seguinte, que na maioria dos computadores tem 8 palavras de comprimento. O mesmo vale para decréscimos:

p1--;

fará com que p1 tenha o valor 1992. Pode-se comparar ponteiros, através dos testes relacionais ">=, <=, < e >". Deve-se, no entanto, tomar cuidado para não comparar ponteiros que apontam para tipos diferentes de variáveis, pois os resultados serão sem sentido. Não se pode multiplicar, dividir, deslocar os bits, somar ou subtrair floats e double aos ponteiros.


PONTEIROS PARA MATRIZES USANDO FUNÇÕES

Vamos analisar como uma função pode usar ponteiros para acessar elementos de uma matriz cujo endereço é passado para a função como argumento. Como exemplo, vamos ver a função adcon1(), que adiciona uma constante a todos os elementos de uma matriz.

  1. include <stdio.h>
#define TAM 5

void adcon1(int *, int, int);

 void main(void)
 {    static int matriz[TAM] = { 3,6,7,9,11};
      int c=10;
      int j;
      adcon1(matriz, TAM, c);
      for(j=0; j<TAM; j++)
             printf("%d", *(matriz+j));

}

// adcon1() - adiciona constante a cada elemento da matriz

void adcon1(int *ptr,int num,int con) { int k;

    for(k=0; k<num; k++)
    {    *ptr = *ptr + con;   ptr++;  }

}


A saída será: 13 16 17 19 21 Na definição da função adcon1(), a declaração " int *ptr; " é equivalente a " int ptr[]; ". Em outras palavras, a primeira declaração cria um ponteiro variável, enquanto a segunda, um ponteiro constante.

Exercícios

1 - "Os ponteiros permitem a passagem de valores por referência para uma função". Demonstre esta propriedade através de um programa.

2 - Faça um programa que calcule a média aritmética de um número arbitrário de notas de provas, usando matrizes.

3 - Repita o programa acima, agora utilizando ponteiros.


Exemplos

/******************************************************************************/ /***** Programa que procura um caractere em uma "string" **************/ /******************************************************************************/

  1. include <stdio.h>
  2. include <conio.h>

char *procstr(char *, char);

void main(void) {

   char *ptr;
   char ch, lin[81];
   puts("Digite uma frase:  ");
   gets(lin);
   printf("Digite o caractere a ser procurado:  ");
   ch = getche();
   ptr = procstr(lin, ch);
   printf("\n A string comeca no endereço %u.\n",lin);
   if(ptr)
   {   printf("Primeira ocorrencia do caractere: %u.\n", ptr);
       printf("E a posição: %d", ptr-lin);
   } 
   else     printf("\n caractere nao existe. \n");
   getche();

}

char *procstr(char *linha,char c) { while(*linha != c && *linha != '\0')

           linha++;
   if(*linha != '\0') 
           return(linha+ 1);
   else 
           return(0);

}