Arquitetura de memória
- Nos computadores atuais a memória é organizada em palavras de tamanho fixo
- Tipicamente cada palavra tem um byte (i.e. 8-bits) \[ \begin{array}{|c|c|c|c|c|c|c|c|} \hline
0 & 1 & 0 & 0 & 1 & 1 & 0 & 1 \\ \hline
\end{array}
\]
- Cada palavra tem um endereço único
- O processador pode aceder a cada palavra individualmente pelo seu endereço
Arquitetura de memória
- Podemos ver os endereços de uma memória com \(n\) palavras como sendo os inteiros de \(0\) até \(n-1\)
- Exemplo: 64Kb de memória (\(=64\times 1024\) bytes)
endereço
|
conteúdo
|
0
|
10101010
|
1
|
11111010
|
\(\vdots\)
|
\(\vdots\)
|
65535
|
11010010
|
Arquitetura de memória
- Como os tamanho de memórias são normalmente potências de 2 é conveniente usar notação hexadecimal (base 16) para os endereços
- Exemplo: os endereços de uma memória de 64Kb vão de
0x0000
até 0xffff
endereço
|
conteúdo
|
0x0000
|
10101010
|
0x0001
|
11111010
|
\(\vdots\)
|
\(\vdots\)
|
0xffff
|
11010010
|
Variáveis
- Em C cada variável ocupa uma ou mais palavras em memória
- O endereço da variável é o endereço da primeira palavra
Exemplo
i
,j
são inteiros
- Supomos que cada inteiro ocupa 2 bytes
- O endereço de
i
é 1000 e o de j
é 1002
Operador &
- O operador
&
obtém o endereço de uma variável
- Podemos imprimir usando
printf
com formato %p
:
int main(void) {
int i, j;
printf("endereço i: %p\n", &i);
printf("endereço j: %p\n", &j);
}
Exemplo:
endereço i: 0x7ffe26476b20
endereço j: 0x7ffe26476b24
(Neste computador cada int
ocupa 4 bytes.)
Apontadores
- Podemos guardar endereços em variáveis declaradas como apontadores
- Quando
p
é um apontador cujo valor é o endereço de i
dizemos que "p
aponta para i
"
Declarar apontadores
- Declaramos uma variável apontador com asterisco:
int *p; /* `p` é um apontador para
valores de tipo `int` */
- Os apontadores devem ser declarados conforme o tipo de valores para que apontam; exemplos:
char *p; // apontador para carateres
double *p; // apontador para doubles
long *p; // apontador para longs
Inicializar apontadores
int *p; // não inicializado
- Declara uma variável como apontador mas não o inicializa
- Tal como outras variáveis, é necessário inicializar apontadores antes de os usar
Inicializar apontadores (cont.)
- Podemos inicializar um apontador atribuindo-lhe o endereço de uma outra variável
int i;
int *p;
...
p = &i;
- Atribuir
&i
a p
faz com que p
aponte para i
:
Operador *
- O operador asterisco (
*
) dá o valor do objeto apontado
- Se
p
aponta para i
então *p
tem o mesmo valor que i
int i, *p;
i = 42;
p = &i;
printf("%d\n", *p); // imprime 42
Operador *
- Podemos também usar
*
no lado-esquerdo duma atribuição para modificar o objeto apontado
- Se
p
aponta para i
, então *p
é um "alias" de i
*p
tem o mesmo valor que i
- modificar
*p
é equivalente a modificar i
int i, *p;
p = &i;
i = 42
*p = 43;
printf("%d\n", i); // imprime 43
Exemplo passo-a-passo
p = &i;

i = 42;

*p = 43;

Operador *
- Usar
*
com apontadores não inicializado resulta em comportamento indefinido
- O programa pode abortar com "Segmentation fault"
int *p;
printf("%d\n", *p); /* ERRO */
int *p;
*p = 1; /* ERRO */
Atribuição de apontadores
- Podemos atribuir um apontador a outro desde que sejam do mesmo tipo:

int i;
int *p, *q;
p = &i;
q = p;
- Após estas instruções
p
e q
apontam para o mesmo objeto (a variável i
)
Atribuição de apontadores
- Se
p
e q
apontam para o mesmo objeto, podemos mudá-lo atribuido a *p
ou a *q


- Podemos ter vários apontadores para um mesmo objeto
Atribuição de apontadores
- Atenção: não confundir as atribuições
q = p;
com
*q = *p;
- A primeira é uma atribuição entre apontadores
- A segunda modifica o valor apontado por
q
(ver figura seguinte)
Atribuição de apontadores
Apontadores como argumentos
- Os argumentos de funções em C são passados por valor
- Isto implica que as modificações dos argumentos não são visíveis depois do retornar da função
- Passar apontadores a uma função permite modificar os objetos apontados
- Isto podem também ser usado quando queremos retornar mais do que um resultado de uma função
Exemplo: trocar duas variáveis
- A seguinte função não funciona
- As variáveis
a
, b
são locais à função
- Logo a troca não tem efeito depois do retorno da função
/* Trocar duas variáveis; versão errada */
void trocar(int a, int b) {
int temp;
temp = a;
a = b;
b = temp;
}
Versão usando apontadores
void trocar(int *pa, int *pb) {
int temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}
- Para invocar temos de passar apontadores para as variáveis a trocar:
int a, b;
...
trocar(&a, &b); // trocar `a' com `b'