AULA 15 - Microcontroladores - Técnico: mudanças entre as edições
imported>Fargoud Sem resumo de edição |
imported>Fargoud |
||
| (9 revisões intermediárias pelo mesmo usuário não estão sendo mostradas) | |||
| Linha 1: | Linha 1: | ||
=PONTEIROS= | |||
Uma das características mais 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? | 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. | |||
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 é: | 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. | 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 | int * pont_i ; //pont_i é um ponteiro para variáveis int | ||
float * pont_f; //pont_f é um ponteiro para variáveis float | float * pont_f; //pont_f é um ponteiro para variáveis float | ||
| Linha 17: | Linha 31: | ||
pont_i = &i; // coloca o endereço de i em pont_i | pont_i = &i; // coloca o endereço de i em pont_i | ||
A variável pont_i agora "aponta para i". | A variável ''pont_i'' agora "aponta para i". | ||
*pont_i = 5; // coloca 5 no endereço apontado por pont_i (i=5) | 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: | Os ponteiros podem ser inicializados já na declaração, como qualquer variável: | ||
| Linha 25: | Linha 41: | ||
int *pi = &i; | int *pi = &i; | ||
Além disto, os ponteiros podem ser utilizados na maioria das expressões válidas em C. | 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 é | |||
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: | |||
#fornecem maneiras com as quais as funções podem realmente modificar os argumentos que recebem; | #fornecem maneiras com as quais as funções podem realmente modificar os argumentos que recebem; | ||
| Linha 35: | Linha 54: | ||
#a notação de ponteiros compila muito mais rapidamente, tornando o código mais eficiente. | #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. | 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> | #include <stdio.h> | ||
void main(void) | void main(void) | ||
| Linha 45: | Linha 70: | ||
py = &y; | py = &y; | ||
printf(" O endereço de x é: %u, e o de y é: %u \n", px, py); | printf(" O endereço de x é: %u, e o de y é: %u \n", px, py); | ||
*px = *px + 10; | *px = *px + 10; // pode-se usar *px += 10; | ||
*py = *py + 10; | *py = *py + 10; // e também (*py ) +=10 | ||
printf(" Agora x é: %d e y é: %d \n", x, y); | printf(" Agora x é: %d e y é: %d \n", x, y); | ||
printf(" ... | printf(" ... agora px aponta p/ %u, e py p/ %u\n", ++px, ++py); | ||
printf(" ... que contém os valores: %d, %d \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. | 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 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 imat[5] = {5,7,31,18,22}; | ||
int *ip; | int *ip; | ||
... | ... | ||
ip = imat; // | 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: | |||
<table class="tg"> | |||
<tr> | |||
<th class="tg-031e">Variável</th> | |||
<th class="tg-031e">Endereço</th> | |||
<th class="tg-031e">Valor</th> | |||
</tr> | |||
<tr> | |||
<td class="tg-031e">imat</td> | |||
<td class="tg-031e">567</td> | |||
<td class="tg-031e">630</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-031e">imat[0]</td> | |||
<td class="tg-031e">630</td> | |||
<td class="tg-031e">5</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-031e">imat[1]</td> | |||
<td class="tg-031e">632</td> | |||
<td class="tg-031e">7</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-031e">imat[2]</td> | |||
<td class="tg-031e">634</td> | |||
<td class="tg-031e">31</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-031e">imat[3]</td> | |||
<td class="tg-031e">636</td> | |||
<td class="tg-031e">18</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-031e">imat[4]</td> | |||
<td class="tg-031e">638</td> | |||
<td class="tg-031e">22</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-031e">ip</td> | |||
<td class="tg-031e">802</td> | |||
<td class="tg-031e">630</td> | |||
</tr> | |||
</table> | |||
então: | então: | ||
Expressão | |||
ip | <table class="tg"> | ||
*ip | <tr> | ||
ip + 1 | <th class="tg-031e">Expressão</th> | ||
*(ip+1) | <th class="tg-031e">Valor</th> | ||
*ip + 1 | <th class="tg-031e">Explicação</th> | ||
imat[0] | </tr> | ||
*(imat) | <tr> | ||
imat[1] | <td class="tg-ugh9">ip</td> | ||
*(imat+1) | <td class="tg-ugh9">630</td> | ||
<td class="tg-ugh9">endereço de imat[0]</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-031e">*ip</td> | |||
<td class="tg-031e">5</td> | |||
<td class="tg-031e">valor em 630</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-ugh9">ip + 1</td> | |||
<td class="tg-ugh9">632</td> | |||
<td class="tg-ugh9">endereço de imat[1]</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-031e">*(ip+1)</td> | |||
<td class="tg-031e">7</td> | |||
<td class="tg-031e">valor em 632</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-ugh9">*ip + 1</td> | |||
<td class="tg-ugh9">6</td> | |||
<td class="tg-ugh9">valor em 630 mais 1</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-031e">imat[0]</td> | |||
<td class="tg-031e">5</td> | |||
<td class="tg-031e"></td> | |||
</tr> | |||
<tr> | |||
<td class="tg-ugh9">*(imat)</td> | |||
<td class="tg-ugh9">5</td> | |||
<td class="tg-ugh9"></td> | |||
</tr> | |||
<tr> | |||
<td class="tg-yw4l">imat[1]</td> | |||
<td class="tg-yw4l">7</td> | |||
<td class="tg-yw4l"></td> | |||
</tr> | |||
<tr> | |||
<td class="tg-rmb8">*(imat+1)</td> | |||
<td class="tg-rmb8">7</td> | |||
<td class="tg-rmb8"></td> | |||
</tr> | |||
</table> | |||
Com strings é a mesma coisa: | Com strings é a mesma coisa: | ||
| Linha 90: | Linha 212: | ||
char *pc="ABC"; | 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'. | 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: | Pode-se ter, também, matrizes de ponteiros: | ||
| Linha 96: | Linha 222: | ||
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. | 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: | Na memória isto ficaria: | ||
<table class="tg"> | |||
<tr> | |||
<th class="tg-xha5">Variável</th> | |||
<th class="tg-xha5">Endereço</th> | |||
<th class="tg-xha5">Valor</th> | |||
</tr> | |||
<tr> | |||
<td class="tg-031e">constante</td> | |||
<td class="tg-031e">731</td> | |||
<td class="tg-031e">'X'</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-031e">constante</td> | |||
<td class="tg-031e">732</td> | |||
<td class="tg-031e">'Y'</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-031e">constante</td> | |||
<td class="tg-031e">733</td> | |||
<td class="tg-031e">'Z'</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-yw4l">constante</td> | |||
<td class="tg-yw4l">734</td> | |||
<td class="tg-yw4l">'\0'</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-yw4l">constante</td> | |||
<td class="tg-yw4l">735</td> | |||
<td class="tg-yw4l">'Q'</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-yw4l">constante</td> | |||
<td class="tg-yw4l">736</td> | |||
<td class="tg-yw4l">'R'</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-yw4l">constante</td> | |||
<td class="tg-yw4l">737</td> | |||
<td class="tg-yw4l">'S'</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-yw4l">constante</td> | |||
<td class="tg-yw4l">738</td> | |||
<td class="tg-yw4l">'\0'</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-yw4l">constante</td> | |||
<td class="tg-yw4l">739</td> | |||
<td class="tg-yw4l">'K'</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-yw4l">constante</td> | |||
<td class="tg-yw4l">740</td> | |||
<td class="tg-yw4l">'L'</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-yw4l">constante</td> | |||
<td class="tg-yw4l">741</td> | |||
<td class="tg-yw4l">'M'</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-yw4l">constante</td> | |||
<td class="tg-yw4l">742</td> | |||
<td class="tg-yw4l">'\0'</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-yw4l">cp[0]</td> | |||
<td class="tg-yw4l">900</td> | |||
<td class="tg-yw4l">731</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-yw4l">cp[1]</td> | |||
<td class="tg-yw4l">902</td> | |||
<td class="tg-yw4l">735</td> | |||
</tr> | |||
<tr> | |||
<td class="tg-yw4l">cp[2]</td> | |||
<td class="tg-yw4l">904</td> | |||
<td class="tg-yw4l">739</td> | |||
</tr> | |||
</table> | |||
==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== | |||
#"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 provas, usando matrizes. | |||
#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= | =ARITMÉTICA DE PONTEIROS= | ||
| Linha 147: | Linha 462: | ||
Como exemplo, vamos ver a função adcon1(), que adiciona uma constante a todos os elementos de uma matriz. | Como exemplo, vamos ver a função adcon1(), que adiciona uma constante a todos os elementos de uma matriz. | ||
#include <stdio.h> | #include <stdio.h> | ||
#define TAM 5 | #define TAM 5 | ||
void adcon1(int *, int, int); | void adcon1(int *, int, int); | ||
void main(void) | void main(void) | ||
| Linha 185: | Linha 500: | ||
Exemplos | Exemplos | ||
/******************************************************************************/ | /******************************************************************************/ | ||
/***** Programa que procura um caractere em uma "string" | /***** Programa que procura um caractere em uma "string" ******/ | ||
/******************************************************************************/ | /******************************************************************************/ | ||
#include <stdio.h> | #include <stdio.h> | ||
#include <conio.h> | #include <conio.h> | ||
char *procstr(char *, char); | char *procstr(char *, char); | ||
void main(void) | void main(void) | ||
{ | { | ||
char *ptr; | char *ptr; | ||
char ch, lin[81]; | char ch, lin[81]; | ||
| Linha 210: | Linha 525: | ||
else printf("\n caractere nao existe. \n"); | else printf("\n caractere nao existe. \n"); | ||
getche(); | getche(); | ||
} | } | ||
char *procstr(char *linha,char c) | char *procstr(char *linha,char c) | ||
{ while(*linha != c && *linha != '\0') | { while(*linha != c && *linha != '\0') | ||
linha++; | linha++; | ||
if(*linha != '\0') | |||
return(linha+ 1); | return(linha+ 1); | ||
else | |||
return(0); | return(0); | ||
} | } | ||
Edição atual tal como às 08h10min de 31 de outubro de 2019
PONTEIROS
Uma das características mais 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:
- fornecem maneiras com as quais as funções podem realmente modificar os argumentos que recebem;
- para passar matrizes e strings mais convenientemente de uma função para outra;
- para manipular matrizes mais facilmente através da movimentação de ponteiros para elas;
- para criar estruturas de dados complexas, como listas encadeadas e árvores binárias, onde uma estrutura de dados deve conter referências sobre outra;
- 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;
- 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
- "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 provas, usando matrizes.
- 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.
#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);
}