Cada posição da memória guarda exatamente 1 byte, e é referenciada por um endereço. O endereço é representado em base hexadecimal.
A memória RAM é o armazenamento primário do computador, onde ficam informações (dados e instruções) dos programas sendo atualmente executados pelo computador. Isso significa, entre outras coisas, que as variáveis dos nossos programas ficam armazenadas na memória durante a execução.
No nosso caso, podemos ver a memória como um grande array de endereços.Como a memória é byte addressed, cada endereço é referente a 1 byte (8 bits).
Cada posição da memória guarda exatamente 1 byte, e é referenciada por um endereço. O endereço é representado em base hexadecimal.
Calculamos o tamanho da memória a partir do tamanho dos endereços. No exemplo acima, um endereço era um número hexadecimal de 4 dígitos:
Como cada byte da memória precisa de um endereço correspondente, calculamos o tamanho da memória vendo quantos endereços diferentes a memória é capaz de endereçar. No exemplo, cada endereço possui 16 bits, que podem representar 216 endereços, ou seja, esta memória é de 64 KiB.
O que determina o tamanho da memória é o tipo de processador:
Podemos utilizar a memória para:
Do ponto de vista da máquina, na memória estão armazenados apenas bits. Veja no exemplo, em que cada byte é representado em base hexadecimal.
Já do ponto de vista do programador, o que está armazenado na memória depende do tipo de variável associado ao endereço que nos referimos.
O tipo da variável define 3 coisas:
Conhecemos variáveis do tipo int, float, double, char, etc. Todos esses tipos de variáveis armazenam dados do programa. Agora vamos conhecer o tipo ponteiro, que armazena endereços de variáveis do programa. Elas são do tipo ponteiro porque apontam para um espaço na memória.
Os tipos ponteiros são tipos inteiros, uma vez que existe um número inteiro de posições da memória. Para declarar um ponteiro para um tipo, acrescentamos um asterisco (*) na frente do nome do tipo.
int* ip; /* ponteiro para inteiro */ char* cp; /* ponteiro para char */ float* fp; /* ponteiro para float */
void é um tipo especial. Não é possível criar variáveis do tipo void, porém, podemos criar variáveis do tipo ponteiro para void. A utilidade é armazenar endereços de variáveis de qualquer tipo
Na prática, isso quer dizer que uma variável do tipo void* pode armazenar o endereço de uma variável do tipo int, ou char, ou double, ou qualquer outro tipo.
Assim, podemos ter ponteiros para qualquer tipo, inclusive para outros ponteiros, isto é, podemos ter ponteiros para ponteiros. Eles são definidos da mesma forma que os ponteiros normais:
int i = 50; /* inteiro */ int* pi; /* ponteiro de inteiro */ int** ppi; /* ponteiro de ponteiro de inteiro */ void* ptr; /* ponteiro void */ void** pptr; /* ponteiro de ponteiro void */
Aprendemos que é possível criar variáveis que armazenam o endereço de outras variáveis. Como vou saber qual é o endereço de uma variável, para que eu possa armazená-lo?
Com o operador &. A linguagem C nos dá este operador que retorna o endereço de uma variável
int main() { int a = 50; int b = 10; int* pi = &a; int c = 520; int** ppi = π return 0; }Code Execution
Agora que já sabemos como armazenar os endereços das minhas variáveis. O que eu faço com eles?
Conhecendo o endereço de uma variável, é possível conhecer o seu valor sem usar seu nome diretamente!
E quando precisarei conhecer o valor de uma variável sem usar seu nome diretamente?
Quando eu conhecer ão o nome, mas apenas o endereço de uma variável, ou seja, em funções!
Através do operador *, fazemos o que chamamos de desreferenciação.
A peração de desreferenciar é auto-explicativa.
Quando possuímos um ponteiro para uma variável, costumamos dizer que possuímos uma referência para esta variável.
Se desreferenciamos um ponteiro, tiramos a referência e passamos a lidar com a variável propriamente dita.
Quando mandamos imprimir o valor de um ponteiro, o que vemos é um número escrito na base hexadecimal.
int main() { int x = 50; int y = 40; int *ptr = &x; printf("%x\n",ptr); return 0; }Code Execution
Ou seja, quando imprimimos o valor de um ponteiro, imprimimos o que ele significa: um endereço de memória.
Quando mandamos imprimir o valor de um ponteiro desreferenciado, o que vemos é o conteúdo da variável para a qual ele aponta.
int main() { int x = 50; int y = 40; int *ptr = &x; printf("%x\n",ptr); int valor = *ptr; printf("%d\n",valor); return 0; }Code Execution
Ou seja, ao desreferenciar um ponteiro, podemos tanto ler o valor da variável apontada, quanto escrever no valor da variável apontada.
int main() { int x = 50; int y = 40; int *ptr = &x; printf("%x\n",ptr); *ptr = 30; int valor = *ptr; printf("%d\n",valor); return 0; }Code Execution
Decimal | Hexadecimal | Binário |
---|---|---|
00 | 0 | 0000 |
01 | 1 | 0001 |
02 | 2 | 0010 |
03 | 3 | 0011 |
04 | 4 | 0100 |
05 | 5 | 0101 |
06 | 6 | 0110 |
07 | 7 | 0111 |
08 | 8 | 1000 |
09 | 9 | 1001 |
10 | A | 1010 |
11 | B | 1011 |
12 | C | 1100 |
13 | D | 1101 |
14 | E | 1110 |
15 | F | 1111 |