endereço | conteúdo |
---|---|
0 | 10101010 |
1 | 11111010 |
\(\vdots\) | \(\vdots\) |
65535 | 11010010 |
0x0000
até 0xffff
endereço | conteúdo |
---|---|
0x0000
|
10101010 |
0x0001
|
11111010 |
\(\vdots\) | \(\vdots\) |
0xffff
|
11010010 |
i
,j
são inteirosi
é 1000 e o de j
é
1002&
&
obtém o endereço de
uma variávelprintf
com formato
%p
:int main(void) {
int i, j;
("endereço i: %p\n", &i);
printf("endereço j: %p\n", &j);
printf}
Exemplo:
endereço i: 0x7ffe26476b20
endereço j: 0x7ffe26476b24
(Neste computador cada int
ocupa 4 bytes.)
p
é um apontador cujo valor é o endereço de
i
dizemos que “p
aponta para
i
”int *p; /* `p` é um apontador para
valores de tipo `int` */
char *p; // apontador para carateres
double *p; // apontador para doubles
long *p; // apontador para longs
int *p; // não inicializado
int i;
int *p;
...
= &i; p
&i
a p
faz com que
p
aponte para i
:*
*
) dá o valor do
objeto apontadop
aponta para i
então *p
tem o mesmo valor que i
int i, *p;
= 42;
i = &i;
p ("%d\n", *p); // imprime 42 printf
*
*
no lado-esquerdo duma atribuição
para modificar o objeto apontadop
aponta para i
, então *p
é um “alias” de i
*p
tem o mesmo valor que i
*p
é equivalente a modificar
i
int i, *p;
= &i;
p = 42;
i *p = 43;
("%d\n", i); // imprime 43 printf
p = &i;
i = 42;
*p = 43;
*
*
com apontadores não inicializado resulta em
comportamento indefinidoint *p;
("%d\n", *p); /* ERRO */ printf
int *p;
*p = 1; /* ERRO */
int i;
int *p, *q;
= &i;
p = p; q
p
e q
apontam para o
mesmo objeto (a variável i
)p
e q
apontam para o mesmo objeto,
podemos mudá-lo atribuido a *p
ou a *q
*p = 1;
*q = 2;
q = p;
*q = *p;
q
(ver figura
seguinte)= &i; q = &j; i = 1; p
*q = *p;
a
, b
são locais à função/* Trocar duas variáveis; versão errada */
void trocar(int a, int b) {
int temp;
= a;
temp = b;
a = temp;
b }
void trocar(int *pa, int *pb) {
int temp;
= *pa;
temp *pa = *pb;
*pb = temp;
}
int a, b;
...
(&a, &b); // trocar `a' com `b' trocar
&
para obter um apontador
para um elemento de uma variável indexadaa
é uma variável indexada, então
&a[i]
é um apontador para o
i
-ésimo elemento de a
:int a[10], *p;
= &a[0]; p
*p = 21;
p
aponta para um elemento de uma variável indexada,
outros elementos podem ser obtidos usando aritmética de
apontadores sobre p
:p
aponta o elemento a[i]
, então
p+j
aponta o elemento a[i+j]
= &a[2];
p
= p + 3; q
p
aponta o elemento a[i]
, então
p-j
aponta o elemento a[i-j]
= &a[2];
p
= p - 2; q
int soma_vec(int vec[], int n) {
int *p, soma;
= 0;
soma for(p = &vec[0]; n>0; n--) {
+= *p;
soma ++;
p}
return soma;
}
int a[10];
*a = 7; // coloca 7 em a[0]
*(a+1) = 12; // coloca 12 em a[1]
Em geral:
a+i |
é equivalente a | &a[i] |
a[i] |
é equivalente a | *(a+i) |
Podemos re-escrever a inicialização do ciclo
for(p = &vec[0]; n>0; n--) {
...
}
como:
for(p = vec; n>0; n--) {
...
}
int soma_vec(int vec[], int n) {
...
}
...
int a[N];
...
= soma_vec(a, N); s
soma_vec(a, N)
passa um apontador à função —
não copia os elementos do vetorsoma_vec
usando um apontador como
argumento:int soma_vec(int *vec, int n) {
...
}
int soma_vec(int vec[], int n) {
...
}
soma_vec
para somar apenas uma parte de um
vetor:int a[100];
...
= soma_vec(&a[30], 10);
s // somar a[30], a[31], ... a[39]
= soma_vec(a+30, 10); s
int *max(int *pa, int *pb) {
if(*pa > *pb)
return pa;
else
return pb;
}
max
passamos dois apontadores:int *p, i, j;
...
= max(&i, &j); p
max
, p
aponta para
i
ou para j
char *f( ... ) {
char tmp[MAX];
...
return &tmp[...]; // ERRO
}
NULL
Definir uma função
char *find_alpha(char *str);
NULL
#include <stdlib.h>
#include <ctype.h>
char *find_alpha(char *str) {
while(*str != '\0' && !isalpha(*str)) {
++;
str }
if (*str != '\0')
return str; // encontrou
else
return NULL; // não encontrou
}
NULL
é uma constante definida no header
stdlib.h
char texto[100], *ptr;
...
= find_alpha(texto);
ptr if(ptr != NULL)
("Primeira letra: %c\n", *ptr);
printfelse
("Não existem letras!\n"); printf
NULL
para inicializar qualquer
apontadorNULL
leva a um erro
de execução (e.g. “segmentation fault”)int *p = NULL;
("%d", *p); // ERRO
printf
*p = 42; // ERRO
Vamos re-implementar algumas funções sobre cadeias usando apontadores:
unsigned comprimento(char *str) {
unsigned len = 0;
char *ptr = str;
while(*ptr != '\0') {
++;
len ++;
ptr }
return len;
}
Versão mais “idiomática”: pós-incremento na condição do ciclo.
unsigned comprimento(char *str) {
unsigned len = 0;
char *ptr = str;
while(*ptr++ != '\0')
++;
lenreturn len;
}
Não é necessário a variável auxiliar ptr
: podemos usar o
argumento da função directamente.
unsigned comprimento(char *str) {
unsigned len = 0;
while(*str++ != '\0')
++;
lenreturn len;
}
unsigned comprimento(char *str) {
char *ptr = str;
while(*ptr++ != '\0');
return ptr-str;
}
void copiar(char *dest, char *origem) {
while(*origem != '\0') {
*dest = *origem;
++;
dest ++;
origem }
*dest = '\0'; // colocar terminador
}
Combinando a atribuição com o pós-incremento de apontadores.
void copiar(char *dest, char *origem) {
while(*origem != '\0')
*dest++ = *origem++;
*dest = '\0'; // colocar terminador
}
void concat(char *dest, char *origem) {
// advançar até final do destino
+= comprimento(dest);
dest // copia a origem
while(*origem != '\0')
*dest++ = *origem++;
*dest = '\0';
}
Resultado 1 se as cadeias são iguais e 0 se são diferentes.
int comparar(char *str1, char *str2) {
while(*str1 != '\0' && *str1 == *str2) {
++;
str1++;
str2}
return (*str1 == *str2);
}