Todos os computadores têm memória, também conhecida por RAM (memória de acesso aleatório). Por exemplo, seu computador pode ter 16, 32 ou 64 megabytes de memória RAM instalados. A memória RAM mantém os programas que atualmente estão em execução no computador, junto aos dados que estão sendo manipulados (suas variáveis e estruturas de dados). A memória pode ser vista como uma matriz de bytes. Nesta matriz, cada local de memória tem seu próprio endereço. O endereço do primeiro byte é 0, seguido por 1, 2, 3, e assim sucessivamente. O endereço de memória atua como os índices de uma matriz normal. O computador pode acessar qualquer endereço na memória, a qualquer momento (por isso o nome "memória de acesso aleatório"). Ele também pode agrupar os bytes necessários para formar matrizes, variáveis e estruturas maiores. Por exemplo, uma variável de ponto flutuante consome 4 bytes contínuos em memória. Você pode fazer a seguinte declaração global em um programa:
float f;
Esta instrução diz: "Declare um local denominado f que possa manter um valor de ponto flutuante”. Quando o programa é executado, o computador reserva espaço para a variável f em algum lugar na memória. Aquele local tem um endereço fixo no espaço de memória, assim:

Enquanto você pensa na variável f, o computador pensa em um endereço específico na memória (por exemplo, 248,440). Assim, ao criar uma instrução como esta:
f = 3.14;
O compilador poderia traduzir isso como: "Carregar o valor 3.14 no local de memória 248,440". O computador está sempre pensando na memória em termos de endereço e valores para tais endereços.
A propósito, existem vários efeitos colaterais interessantes na forma com a qual seu computador gerencia a memória. Por exemplo, suponhamos que você incluiu a seguinte codificação em um de seus programas:
int i, s[4], t[4], u=0;
for (i=0; i<=4; i++)
{
s[i] = i;
t[i] =i;
}
Printf("s:tn");
for (i=0; i<=4; i++)
printf("("%d:%dn", , s[i], t[i]);
printf("u = %dn", u);
O resultado que você vê no programa provavelmente será algo assim:
s:t
1:5
2:2
3:3
4:4
5:5
u = 5
Por que t[0] e u estão incorretos? Se olhar cuidadosamente o código, você verá que os loops for estão escrevendo um elemento além do final de cada matriz. Na memória, as matrizes são colocadas lado a lado, como mostrado abaixo:
![]() |
Logo, ao tentar escrever para s[4], que não existe, o sistema escreve para t[0], uma vez que t[0] é onde s[4] deveria estar. Ao escrever para t[4], na verdade você está escrevendo em u. No que se refere ao computador, s[4] é simplesmente um endereço e pode ser escrito. Como você pode ver, mesmo que o computador execute o programa, ele não está correto ou válido. O programa corrompe a matriz t no processo de execução. A execução da seguinte instrução pode resultar em conseqüências mais graves:
s[1000000] = 5;
O local s[1000000] provavelmente está fora do espaço de memória de seu programa. Em outras palavras, você está escrevendo na memória que seu programa não possui. Em um sistema com espaços protegidos de memória (UNIX, Windows 98/NT), este tipo de instrução fará com que o sistema encerre a execução do programa. Em outros sistemas (Windows 3.1, Mac), todavia, o sistema não sabe o que você está fazendo. Você acaba danificando o código ou as variáveis em outro aplicativo. O efeito da violação pode variar de nenhum a uma tremenda pane do sistema. Na memória, i, s, t e u são todos colocados próximos uns aos outros em endereços específicos. Portanto, se você escrever além dos limites de uma variável, o computador fará o que foi solicitado, porém acabará corrompendo outro local de memória.
As linguagens C e C++ não executam qualquer tipo de verificação de extensão ao acessar um elemento na matriz. É essencial que você, como programador, preste atenção às extensões de matriz e respeite seus limites. A leitura ou escrita não-intencional fora dos limites da matriz quase sempre leva ao comportamento errôneo do programa.
Como outro exemplo, tente o seguinte:
#includeint main() { int i,j; int *p; /* um ponteiro para um inteiro */ printf("%d %dn", p, &i); p = &i; printf("%d %dn", p, &i); return 0; }
Este código diz ao compilador para imprimir o endereço contido em p, junto com o endereço de i. A variável p começa com um valor estranho ou com 0. O endereço de i geralmente é um valor alto. Por exemplo, quando executei este código, recebi o seguinte resultado:
0
2147478276
2147478276
2147478276
que significa que o endereço de i é 2147478276. Após executar a instrução p = &i;, p contém o endereço de i. Tente também:
#includevoid main() { int *p; /* um ponteiro para um inteiro */ printf("%dn",*p); }
Este código diz ao compilador para imprimir o valor para o qual p aponta. Porém, p ainda não foi inicializado. Ele contém o endereço 0 ou algum endereço aleatório. Na maioria dos casos, resulta em uma falha de segmentação (ou algum outro erro no tempo de execução), o que significa que você usou um ponteiro que aponta para uma área inválida da memória. Quase sempre, um ponteiro não inicializado ou um endereço de ponteiro inválido é a causa das falhas de segmentação.
Dito isso, agora podemos ver os ponteiros por uma ótica totalmente diferente. Veja este programa, por exemplo:
#includeint main() { int i; int *p; /* um ponteiro para um inteiro*/ p = &i; *p=5; printf("%d %dn", i, *p); return 0; }
Eis o que está acontecendo:
A variável i consome 4 bytes de memória. O ponteiro p também consome 4 bytes (na maioria das máquinas atuais, um ponteiro consome 4 bytes de memória; os endereços de memória tem 32 bits de extensão na maioria das CPUs, embora exista uma tendência de crescimento para o endereçamento de 64 bits). O local de i tem um endereço específico, neste caso 248,440. O ponteiro p contém aquele endereço, uma vez que você disse que p = &i;. As variáveis *p e i são, portanto, equivalentes.
O ponteiro p literalmente contém o endereço de i. Você pode dizer algo assim em um programa:
printf("%d", p); o resultado é o endereço corrente da variável i.