Ponteiros
Linguagem de Programação Estruturada Prof. Leandro Carlos Fernandes
2º semestre 2015
Ponteiros
• Um ponteiro é uma variável que contém (armazena) um endereço de memória
• Declaração:
tipo_dado *nome_ponteiro;
onde "*" indica que a variável é um ponteiro
• Ex: int x;
int *px; /* compilador sabe que px é ponteiro */
/* px é um ponteiro para inteiro */
Ponteiros
• O operador “&” quando aplicado sobre um identificador (nome de variável, por exemplo) retorna o seu endereço
• Ex: int x = 10, *pi;
pi = &x;
printf("&x: %p pi: %p", &x, pi);
• Saída em tela:
&x: 0x03062fd8 pi: 0x03062fd8
• O operador “*” quando aplicado sobre um ponteiro retorna o dado apontado
• Ex:
int main () { int *tmp_ptr;
int x, y;
x = 10;
tmp_ptr = &x;
y = *tmp_ptr;
return 0;
}
Ponteiros
0xABA0 0xABA0
10 10 10 10
0xABA2 0xABA0
:
x
y tmp_ptr 0xA000
Ponteiros
• ponteiros são variáveis tipadas:
(int *) ≠ (float *) ≠ (char *)
• Ex:
intmain() { int*ip, x;
float*fp, z;
ip = &x; /* OK */
fp = &z; /* OK */
ip = &z; /* erro */
fp = &x; /* erro */
return0;
}
(int *)
(float *)
1 byte (char *)
Utilizando Ponteiros
#include <stdio.h>
intmain() { intx = 10;
int*pi;
pi = &x; /* *pi é igual a 10 */
(*pi)++; /* *pi é igual a 11 */
printf("%d"x);
return0;
}
ao alterar *pi estamos alterando o conteúdo de x
Utilizando Ponteiros
#include <stdio.h>
intmain() { intx = 10;
int*pi, *pj;
pi = &x; /* *pi == 10 */
pj = pi; /* *pj == 10 */
(*pi)++; /* (*pi, *pj, x) == 11 */
(*pj)++; /* (*pi, *pj, x) == 12 */
printf("%d", x); /* Escreverá 12 */
return0;
}
PONTEIROS & PARÂMETROS DE
FUNÇÕES
Passagem de Informações
• Argumentos passados por referência
– Quando chamada, a função passa a referenciar (apontar) a variável informada
– Portanto o processo consiste em informar o endereço da variável para o que o parâmetro formal possa referenciá-lo.
– Os argumentos deixam de existir após a execução do método, porém as variáveis informadas e que foram referenciadas permanecem (pois não são dadas por este bloco de comandos).
9
Exemplo
#include <stdio.h>
/* Protótipos */
void funcPorVarlor(inta);
void funcPorRefer(int *b);
intmain () { intx = 0, y = 0;
funcPorValor(y);
printf("%d %d\n", x, y);
funcPorRefer(&y);
printf("%d %d\n", x, y);
return0;
}
/* Definição das subrotinas */
void funcPorVarlor(inta){
a = 1;
}
void funcPorRefer(int *b){
*b = 2; /* ... o conteúdo apontado por b recebe 2 */
}
• Note que as variáveis xe ysão locais a função main, enquanto os parâmetros ae bsão locais a funcPorValore funcPorRefer, respectivamente.
PONTEIROS & ARRAYS
Referenciando Arrays
• Pode-se referenciar os elementos de um array através de ponteiros
• Ex: float m[] = { 1.0, 3.0, 5.75, 2.345 };
float *pf;
pf = &m[2];
printf("%f", *pf); /* Escreve 5.75 */
Referenciando Elementos
• Pode-se utilizar ponteiros e colchetes:
float m[] = { 1.0, 3.0, 5.75, 2.345 };
float *pf;
pf = &m[2];
printf("%f", pf[0]); /* ==> 5.75 */
• Note que o valor entre colchetes é o
deslocamento a ser considerado a partir do endereço de referência
• pf[n] => indica n-ésimo elemento a partir de pf
Aritmética de Ponteiros
• É possível fazer operações aritméticas e relacionais entre ponteiros e inteiros
• Soma: ao somar-se um inteiro n a um ponteiro, endereçamos n elementos a mais (n positivo) ou a menos (n negativo)
• Assim, temos:
pf[2] equivale a *(pf+2)
*(pf + n) endereça n elementos a frente
*(pf - n) endereça n elementos atrás pf++ endereça próximo elemento array pf-- endereça elemento anterior array
Exemplo
#include <stdio.h>
int main () {
int arint[] = { 1,2,3,4,5,6,7 };
int size = 7; /* tamanho do array */
int i, *pi;
for (pi=arint, i=0; i < size; i++, pi++) printf("%d ", *pi);
return 0;
} /* saída: 1 2 3 4 5 6 7 */
Exemplo - Variação #1
#include <stdio.h>
int main () {
int arint[] = { 1,2,3,4,5,6,7 };
int size = 7; /* tamanho do array */
int i, *pi;
for (pi=arint, i=0; i < size; i++) printf("%d ", *pi++);
return 0;
} /* saída: 1 2 3 4 5 6 7 */
Exemplo - Variação #2
#include <stdio.h>
int main () {
int arint[] = { 1,2,3,4,5,6,7 };
int size = 7; /* tamanho do array */
int i, *pi;
pi = arint;
printf("%d ", *pi); pi+=2;
printf("%d ", *pi); pi+=2;
printf("%d ", *pi); pi+=2;
printf("%d ", *pi);
return 0;
} /* saída: 1 3 5 7 */
Operações Válidas Sobre Ponteiros
É válido
• somar ou subtrair um inteiro a um ponteiro
(pi ± int)
• incrementar ou decrementar ponteiros
(pi++, pi--)
• subtrair ponteiros (produz um inteiro)
(pf - pi)
• comparar ponteiros
( >, >=, <, <=, == )
Não é válido
• somar ponteiros
(pi + pf)
• multiplicar ou dividir ponteiros
(pi*pf, pi/pf)
• operar ponteiros com double ou float
(pi ± 2.0)
Ponteiros e Strings
• Strings são arrays de caracteres e podem ser acessados através de char *
int main() {
char str[] = "abcdef", *pc;
for (pc = str; *pc != '\0'; pc++) putchar(*pc);
return 0;
}
• O incremento de pc o posiciona sobre o próximo caractere (byte a byte)
Array de Strings
• Neste caso, cada elemento do array é um ponteiro para um caractere.
• Declaração:
char *arstr[] = {"Joao", "Maria", "Antonio",
"Zacarias", "Carlos"};
• Neste caso arstr é um array de ponteiros para
char, iniciado com os strings indicados
Array de Strings
• Comparando array de string com matriz de char
char
*as[] = {"Joao","Maria","Antonio","Zacarias","Carlos"};
char
mc[5][10] = {"Joao","Maria","Antonio","Zacarias","Carlos"};
Ponteiros (as)
J o a o M a r i a A n t o n i o Z a c a r i a s C a r l o s
\0
\0
\0
\0
\0
Matriz (mc)
J o a o M a r i a A n t o n i o Z a c a r i a s C a r l o s
\0
\0
\0
\0
\0
Cuidados com Strings
• É comum esquecer de alocar uma área para armazenamento de caracteres
int
main() {
char
*pc; char str[] = "Um string";
strcpy(pc, str); /* erro! pc indeterminado */
...
return
0;
}
Registros (Structs)
SCE283 – Ling. de Programação e Aplicações
Prof. Leandro Carlos Fernandes2º semestre 2008
Estruturas
• Structs são coleções de dados heterogêneos agrupados em um mesmo elemento de dados
• Ex: armazenar as coordenadas (x,y) de um ponto:
24
(x, y)
Estruturas
• Declaração:
• A estrutura contém dois inteiros, x e y
• Neste caso, a estrutura foi definida e com ela duas variáveis, p1 e p2, foram declaradas (cada um contendo duas coordenadas).
25
(x, y)
struct
{
intx;
int
y;
} p1, p2;
Declaração
• Formato da declaração:
• A estrutura pode agrupar um número arbitrário de dados de tipos diferentes
• Pode-se nomear a estrutura para aumentar a facilidade em referenciá-la
26
structnome_da_estrutura
{ tipo_1 dado_1;
tipo_2 dado_2;
...
tipo_n
dado_n;} lista_de_variaveis;
Nomeando uma Estrutura
27
structponto { intx;
inty;
};
structponto p1, p2;
Para evitar a repetição
struct pontodefine um novo tipo de dado Pode-se definir novas variáveis do tipo ponto struct
{
int
x;
int
y;
} p1;
struct
{
intx;
int
y;
} p2;
Estruturas
• Assim como as demais variáveis compostas, temos de ter a capacidade de manipular seus elementos individualmente.
• Acessando os dados:
nome_variavel_struct.campo
• Ex: p1.x = 10; /*atribuição */
p2.y = 15;
if (p1.x >= p2.x) && (p1.y >= p2.y) ...
28
Atribuição de Estruturas
• Tal qual a demais variáveis, é possível inicializar uma estrutura no momento de sua declaração:
struct
ponto p1 = { 220, 110 };
• A operação de atribuição entre estruturas do mesmo tipo pode acontecer de maneira direta:
struct
ponto p1 = { 220, 110 };
struct
ponto p2;
p2 = p1;
/* p2.x = p1.x e p2.y = p1.y */–
Note que os campos correspondentes das estruturas são automaticamente copiados do destino para a fonte
29
Espaço alocado para uma Estrutura
struct aluno {
char nome[20]; /* array 20 bytes */
int idade; /* 4 bytes */
char matricula[8]; /* array 8 bytes */
};
struct aluno al;
strcpy( al.nome, "Assis“);
al.idade = 21;
strcpy( al.matricula, "00/0001");
30 struct aluno al
"Assis"
21
"00/0001"
Composição de Estruturas
• De fato, as structs definem novos tipos de dados (tipos do usuário) e portanto podem conter campos de qualquer tipo, quer sejam tipos básicos ou outros tipos definidos pelo usuário.
• Inclusive, suportam a definição de estruturas compostas de outras estruturas!
– Um retângulo poderia ser definido por dois pontos: o superior esquerdo e o inferior direito.
Composição de Estruturas
Acesso aos dados:
32
struct
retangulo {
struct
ponto cantoSupEsq;
struct
ponto cantoInfDir;
};
struct
retangulo r = { { 10, 20 }, { 30 , 40 } };
r.cantoSupEsq.x += 10;
r.cantoSupEsq.y -= 10;
STRUCTS & SUBROTINAS
Estruturas como retorno de função
struct ponto cria_ponto (int x, int y) { struct ponto tmp;
tmp.x = x;
tmp.y = y;
return tmp;
}
int main () {
struct ponto p = cria_ponto(10, 20);
...
return 0;
}
34
Estruturas como parâmetros para funções
• As operações entre membros das estruturas devem ser feitas membro a membro:
/* retorna uma cópia de p1 = p1 + p2 */
struct ponto soma_pts (struct ponto p1, struct ponto p2) { p1.x += p2.x;
p1.y += p2.y;
return p1; /* retorna uma copia de p1 */
}
35
Ponteiros para Estruturas
• Estruturas grandes são passadas como
parâmetro de forma mais eficiente através de ponteiros
struct ponto *pp;
struct ponto p1 = { 10, 20 };
pp = &p1;
printf("Ponto P1: (%d %d)\n", (*pp).x, (*pp).y};
• acesso via operador "->":
printf("Ponto P1: (%d %d)\n", pp->x, pp->y};
36
Exemplo
#include <stdio.h>
#include <stdlib.h>
structponto { intx,y;
};
voidsomaPorValor(structponto p, inta){
p.x += a;
p.y += a;
}
voidsomaPorRef(structponto *p, inta){
p->x += a;
p->y += a;
}
voidmain() {
structponto r = {0,0}, q;
q.x = 1; q.y = 2;
printf("r(%d,%d) q(%d,%d)", r.x, r.y, q.x, q.y);
r = q;
printf("r(%d,%d) q(%d,%d)", r.x, r.y, q.x, q.y);
somaPorValor(q,2);
printf("q(%d,%d)\n", q.x, q.y);
somaPorRef(&q,2);
printf("q(%d,%d)\n", q.x, q.y);
system("pause");
}
37
Exemplo
38