Nos seus próprios estudos (por conta própria ou para uma aula), você teve um momento "ah ha" quando finalmente entendeu realmente os indicadores? Você tem uma explicação usada para programadores iniciantes que parece particularmente eficaz?
Por exemplo, quando os iniciantes encontram ponteiros em C, eles podem adicionar &
s e *
s até compilar (como eu mesmo fiz uma vez). Talvez tenha sido uma imagem ou um exemplo muito bem motivado que fez os ponteiros "clicarem" para você ou seu aluno. O que foi e o que você tentou antes que parecia não funcionar? Houve algum pré-requisito de tópicos (por exemplo, estruturas ou matrizes)?
Em outras palavras, o que era necessário entender o significado de &
s e *
, quando você poderia usá-los com confiança? Aprender a sintaxe e a terminologia ou os casos de uso não é suficiente; em algum momento, a ideia precisa ser internalizada.
Atualização: eu realmente gosto das respostas até agora; por favor, mantenha-os chegando. Existem muitas perspectivas excelentes aqui, mas acho que muitas são boas explicações / slogans para nós mesmos depois de internalizarmos o conceito. Estou procurando os contextos e circunstâncias detalhados quando isso ocorreu a você.
Por exemplo:
Eu só entendi um pouco os ponteiros sintaticamente em C. Ouvi dois de meus amigos explicando os ponteiros para outro amigo, que perguntou por que um
struct
passador foi passado com um ponteiro. O primeiro amigo falou sobre como precisava ser referenciado e modificado, mas foi apenas um breve comentário do outro amigo em que me ocorreu: "Também é mais eficiente". Passar 4 bytes em vez de 16 bytes foi a mudança conceitual final que eu precisava.
fonte
Respostas:
Diagrama de memória como grade
Normalmente, o que faço é representar a memória como uma "grade", para que eu possa criar endereços, destacar diferentes espaços de memória e escrever nos valores das células (ou ainda mais, suas representações binárias) e vincular os ponteiros na memória aos valores que eles apontar para. (E depois ainda mencione que é uma simplificação).
Geralmente é um momento "ohhhh" para a maioria dos meus alunos.
Malabarismo com símbolos
Então, quando eles param de esquecer como usar & e *, é muito simples: apresente da mesma maneira que eles fazem cálculos de matemática ou física. Se você dividir uma distância em km por uma hora em hora, obtém uma velocidade em km / h. O que está dentro precisa estar fora. Simples.
printf para o Rescue
Fazer apenas alguns exemplos básicos que representam visualmente o que você explicou com eles os confortará no que eles pensam que entenderam, ou dará a eles a oportunidade de dizer "ah, eu não entendo esse".
Seja extenso
Cubra ponteiros para tipos simples e verifique se eles entendem a diferença entre endereçamento e tamanho de um tipo de dados, estruturas, matrizes e vários níveis.
Em seguida, inicie a aritmética do ponteiro.
Adendo: Recursão
Eu normalmente explico a recursão da mesma forma, usando uma representação visual. Peça que imprimam o alfabeto usando uma função pré-criada que escreve um único caractere e, em seguida, peça para que imprimam em ordem inversa, alterando apenas duas linhas.
Normalmente, há um "o que o ...?" momento, e quando você adiciona apenas outro parâmetro ao seu printf para imprimir valores numéricos e recuar as etapas, ele se torna um suspiro de alívio.
Alternativas: o modelo Play-Doh e os copos de água
Na verdade, tive alguns colegas de trabalho em uma universidade que mostraram aos alunos um vídeo explicando ponteiros e acessos à memória usando a pasta play-doh. Foi incrivelmente inteligente e bem-feito, embora eu nunca tenha realmente usado essa técnica, exceto por alunos muito jovens interessados em entender a programação (mas geralmente aqueles que eu não os levaria a uma linguagem usando ponteiros muito cedo). Basicamente, usando pequenas bolas de brincadeira que você pode anexar a outras bolas maiores de brincadeira representando espaços de memória, e que você pode combinar para vinculá-las (como em uma estrutura de dados vinculada) ou mesclar (como em um contíguo espaço de memória). O uso de cores diferentes para os espaços de memória apontados e os ponteiros também ajudam. Mas ainda acho que a memória como grade funciona melhor, como você pode mostrar claramente que a indicação é realmente uma questão de "endereçar", como em um "mapa / grade". Enquanto o modo Play-doh ainda os confunde, pensando que as coisas realmente "se tocam" na memória.
A coisa Water Cup também foi usada diretamente por uma colega, mas não sei se ela a sugeriu. Foi uma abordagem interessante, mas notei que muitos alunos ficaram intrigados com a explicação. Algo parecido com a técnica da xícara de café da DevSolo . Mas acho que é realmente enganador, pois você faz os alunos confundirem contêineres, estruturas de dados, ponteiros e matrizes. Pode ser uma abordagem interessante para explicar matrizes no início, presumo, mas não me ateria a ela por muito tempo.
fonte
Alguém muito mais sábio do que eu disse uma vez:
fonte
Eu achei que os diagramas eram muito úteis. Exemplo:
Esse tipo de diagrama me ajudou a ver que os ponteiros eram sua própria variável, mas continham um valor que era a localização de outro objeto, ou seja, matriz ou string. Além disso, quando feito a lápis, eu poderia usá-lo para rastrear meu programa em papel ou em um quadro-negro / quadro branco.
fonte
Quando eu "aprendi" sobre ponteiros pela primeira vez, fiquei meio empolgado. Minha universidade tomou a decisão muito tempo antes de eu me matricular para centralizar o currículo em Java; portanto, quando meu professor de Estruturas de Dados deu uma palestra em C e nos pediu para implementar uma lista XOR com indicadores, senti que estava entrando em algo muito acima da minha cabeça.
Eu entendi a definição:
Mas ainda não entendi grande parte do conceito. Olhando para trás, acho que estava centrado em três coisas:
O que exatamente é um local de memória? (Na época não participei de uma aula de Organização de Computadores)
A sintaxe incômoda (Então, hum ... por que exatamente ela é definida como "int * ip", mas depois me refiro à variável como "ip"?)
Como exatamente é benéfico armazenar o endereço de uma variável em vez de apenas usá-la?
Não foi até eu comprar o livro K&R e completar todos os problemas que eu realmente compreendi as dicas. Além do fato de eu ter concluído há muito tempo a aula de Organização do Computador (que acho que deveria ser necessária antes de aprender C), parte disso tinha a ver com o fato de perceber que os ponteiros podem ser usados para funções, matrizes, estruturas ... fazer coisas úteis e não apenas como armazenamento para endereços de variáveis comuns.
Mas o meu momento "aha" foi de longe o modo como a K&R explicou a sintaxe incômoda de definir um ponteiro simples. Tomei notas ao longo do livro (em que refizendo as observações feitas pelo livro em minhas próprias palavras, a fim de aprofundar minha compreensão), e este é o que diz respeito a isso:
Eu sempre senti que não podia entender completamente os conceitos mais avançados de ponteiros até ter algo tão elementar enraizado na minha cabeça. Isso me incomodou incessantemente depois daquela tarefa (a qual não fiz muito bem, aliás;)) por que "* ip" não existia logo depois que eu (pensei que eu) defini "* ip". Dominar isso é essencial para conceitos mais avançados que envolvem ponteiros, como ponteiros de função e definições mais complicadas como esta:
Em suma, acho que o conceito de ponteiros requer:
Um entendimento básico de como a memória é organizada em um computador (e o que é memória).
Conhecimento de quão poderosos os ponteiros podem ser (uso no mundo real, não apenas outro conceito abstrato que eles aprendem para aprender).
Decifrando os hieróglifos conhecidos coloquialmente como "uma definição de ponteiro"
Então, apesar de tudo, acho que deveria haver pelo menos três momentos "aha" ao aprender sobre ponteiros. Ainda sou estudante, então pensei que você apreciaria o ponto de vista de alguém que ainda está (relativamente) fresco aprendendo o conceito.
fonte
PointerTo<int> a
]int* ip
não seja mais comum. Finalmente comecei a entender os ponteiros depois que vi um código formatado dessa maneira. Então eu poderia lê-lo como "ip é do tipo int pointer".O ponteiro é um pouco como os atalhos de aplicativos na área de trabalho. Exclua o atalho e o destino ainda existe. Inicie o atalho e o destino será iniciado.
Eu sempre explico o funcionamento disso criando um arquivo txt na minha área de trabalho e dois atalhos para o arquivo. Após copiar e excluir os atalhos, você pode ver as pessoas entenderem a ideia por trás de 'referências'.
Quando o grupo entender o básico por trás dos atalhos, você poderá começar a explicar os ponteiros da maneira que desejar. Eles provavelmente entenderão isso facilmente.
fonte
8 a 10 anos atrás, eu ensinei um curso de introdução "C" em uma faculdade comunitária. Este sempre foi um tópico divertido de explorar. O que parecia funcionar melhor, e isso foi depois de discutir algumas vezes com um colega de trabalho: usar uma xícara de café e sua mão.
Eu usei a analogia de uma xícara de café (ou uma linha delas para matrizes) como uma variável (pode conter alguma coisa). Eu então usei minha mão, que também poderia segurar algo ou, estendendo meu dedo indicador para "apontar para" uma xícara de café.
Uma mão fechada era nula, um dedo apontando para minha cabeça (como uma arma simulada) era um ponteiro pendurado.
Depois, com algumas demonstrações e viagens pelo depurador, ele clicou com a maioria.
fonte
Fico realmente preocupado quando ouço a pergunta "como você conseguiu entender os ponteiros". Eu sempre achei o conceito incrivelmente simples e uma evolução lógica das linguagens, fornecendo grande poder aos programadores.
O que me preocupa é que nunca achei difícil entender o conceito de ponteiros. Então, quando você aqui "quando finalmente conseguiu" repetidamente, começa a pensar:
Talvez a razão pela qual o conceito pareça complicado, seja porque continuamos dizendo a todos que ainda os encontraram que os indicadores são tão difíceis, e aqui estão centenas de maneiras pelas quais você pode aprender?
Apenas lançando essa idéia, é claro que pessoalmente adoro um bom diagrama e Steve Gibson sempre faz um trabalho fantástico de explicar qualquer coisa !
fonte
Eu nunca tive muito problema com ponteiros em C, mas isso pode ser porque eu tive que aprender primeiro o assembler. Na verdade, foi um alívio não ter mais que administrar meus endereços. Talvez a resposta (supondo que isso seja algo que você esteja ensinando) seja oferecer aos alunos uma linguagem assembly emulada para trabalhar. Eles vão descobrir isso no processo de escrever qualquer coisa mais sofisticada do que "Olá, mundo".
fonte
Um ponteiro é uma variável cujo valor é o endereço de memória de outra variável.
fonte
Como eu realmente aprendi sobre ponteiros? Ao escrever um compilador simples, o primeiro ano de faculdade.
Como explicar os ponteiros em termos leigos? Gosto da analogia (datada?) De um catálogo de biblioteca armazenado em cartões de índice. Cada cartão ("ponteiro") contém informações sobre a localização de algum livro ("dados"), mas na verdade não contém o próprio livro. Se você modificar o cartão ("aritmética do ponteiro"), tudo o que ele faz é alterar para qual livro ele aponta e não tem impacto no próprio livro - apenas tome cuidado para não estragar o endereço ou você pode apontar para um livro inexistente ou mesmo a biblioteca errada. No entanto, se você seguir o "endereço" no cartão e for para a parte apropriada da biblioteca ("desreferenciar o ponteiro"), poderá ver / modificar o próprio livro.
fonte
Suponho que alguém que vai aprender ponteiros sabe quais são as variáveis normais e como elas funcionam em C. Agora, vamos tentar definir ponteiros com alguns de seus atributos -
Eles também são variáveis, mas de natureza diferente. Suponha que haja duas camadas no espaço variável. As variáveis normais de diferentes tipos residem na camada superior e ponteiros na camada inferior. Como esta figura-
Como o nome 'Ponteiro' sugere, os ponteiros podem apontar para alguma coisa. Como nosso dedo pode apontar para algum objeto. Quais são as coisas para as quais eles apontam? Essas são as variáveis normais. Em resumo, "Ponteiros apontam para variáveis normais".
Então, como um ponteiro é declarado? Quase como variáveis normais. Você deve preceder o nome com um asterisco (
*
). Gostar-Então, como um ponteiro está associado a uma variável? Usando o
&
operador antes da variável como esta declaraçãoComo um ponteiro é usado apontando para uma variável? Isso também é feito precedendo o nome com um asterisco (
*
). Em seguida, ele pode ser usado no lugar da variável que está apontando agora -ao invés de
Agora, brinque com ponteiros com algum código. Acostume-se a essas características dos ponteiros agora. Até este ponto, estamos falando sobre o que os ponteiros fazem. Quando estiver de acordo, comece a estudar como os ponteiros estão apontando para alguma variável e como eles reagem se operações aritméticas normais forem aplicadas a elas. Em seguida, vá para a relação entre ponteiros e matrizes e ponteiros para ponteiros.
Isso é tudo o que vou sugerir sobre o aprendizado de indicadores.
fonte
Editado em suporte ao requisito revisado da pergunta
Meu caminho para o entendimento "pós-ponteiro" (se bem me lembro) foi assim. Eu tive uma experiência simples de programação de montagem de quando ainda estava mexendo com um BBC Micro, então tive o conceito de memória como um monte de caixas (veja abaixo). Isso foi reforçado pelo uso de matrizes. No entanto, eu estava entrando no mundo do C e tive que lidar com strings, o que significava ponteiros. No BASIC, isso foi trivial, no assembler eu nunca tive que trabalhar com eles, e agora no clássico C são todos indicadores e outras coisas. Ah, mas posso voltar às matrizes com strings (terminação nula) como esta:
Tudo bem, isso é apenas uma matriz de caracteres (8 bits por caractere no meu pequeno mundo) com um caractere zero no final para mostrar onde termina. O printf apenas pega esse array e passa por cima dele, imprimindo-o. Mas e se eu quiser passar essa string para uma função?
É o que o manual diz, mas o que é isso *? Hmm, char * significa "ponteiro para um char". OK ... me perdi. Então me ocorreu que o caractere * faz p equivalente a s [0]. OK, eu posso usar isso, mas ainda não entendi o que são indicadores. Quero dizer, como faço para definir alguns dados com uma dessas coisas? Off para o manual novamente ...
Enquanto escrevo o texto acima, estou dizendo a mim mesmo "declare um ponteiro para um caractere e defina-o como esse conjunto de caracteres". De alguma forma, esse ponteiro elimina a necessidade de uma matriz. Acho que é o compilador fazendo algumas coisas para mim, para variar. Então, como posso alterar a matriz com esse ponteiro? Eu sei que eu poderia usar a versão do array
mas qual é o equivalente em "falar com ponteiro"? Fora para esse manual novamente ...
Suponho que * significa simplesmente "o conteúdo do endereço de memória" e
(p+2)
és[2]
. O que significava que o ponteiro p era ... apenas ... um ... endereço ... bong!Que existe o som da iluminação. De repente, girei ponteiros (foi há muito tempo, nós, veteranos, não "grocávamos" até mais tarde). Foi apenas indireto.
Original:
OK, meu valor 2c:
Imagine que a memória é um monte de caixas. Cada caixa tem um número ao lado (o endereço). Cada caixa contém um número (o conteúdo). Você pode trabalhar com essas caixas de duas maneiras: variáveis (eu quero o conteúdo da caixa N), ponteiros (eu quero o conteúdo da caixa, o que estiver na caixa N ). Ponteiros são simplesmente indiretos.
E para a grande resposta que cobre tudo o que você precisará saber - leia isso .
fonte
Pegue alguns pequenos blocos de madeira.
adicione ganchos de metal em uma extremidade e olhos de metal na outra.
agora você pode fazer uma lista vinculada de itens com os quais pode brincar.
Tente explicar com este suporte físico. Eu sempre desejei ter isso ao ensinar sugestões para os alunos do primeiro ano.
O gancho de metal é o ponteiro, o bloco de madeira que a coisa apontou.
Eu confio em alguém para não conseguir depois de brincar com os blocos.
fonte
Eu facilitei minha vida quando apenas removi todo o cotão e comecei a tratar o ponteiro como qualquer outra variável, e não como uma entidade mágica (há muito tempo na série 11) .. apenas saiba três coisas:
O resto é açúcar sintático e bom senso. Basta escrever alguns programas C simples (como implementar uma biblioteca de listas vinculadas) usando ponteiros para entender o problema.
fonte
fonte
Não me lembro bem das circunstâncias em torno dos meus ponteiros, aha-moment, mas reajustei retroativamente a memória em torno da minha compreensão de uma matriz de estilo c. (ou seja,
arr[3]
é o mesmo que*(arr+3)
)Por alguma razão, acho imensamente útil visualizar ponteiros como matrizes sempre que me deparei com uma situação de ponteiro.
fonte
Basicamente, Gary Rowe apresentou o modelo certo. Memória como um conjunto de caixas com endereços (números) nelas. Cada caixa armazena um valor (número).
A idéia de um ponteiro é interpretar o valor em uma caixa como o endereço de outra caixa. Este valor é usado para se referir a uma caixa específica, razão pela qual é chamada de referência . Portanto, a desreferenciação é o processo de abertura da caixa a que se refere.
se
v
for uma variável (caixa), a instruçãov
significa valor dev
, ou seja, me dê o que está na caixa*v
significa desreferenciar o valor dev
, ou seja, me dê o que está na caixa referida pelo valor dev
&v
significa referênciav
, ou seja, me dê o endereço na caixaEu acho que não tem como objetivo introduzir ponteiros como algo completamente diferente. Era um conceito difícil de entender para mim quando eu era criança. Sempre pareceu uma magia negra que eu nunca entendi, que precisava de muitos personagens especiais. A primeira vez que entendi foi quando escrevi um pequeno jogo usando o duplo indireção em uma linguagem que não possui aritmética de ponteiro. Isso me iluminou.
Ponteiros são uma questão de interpretação. Eu acho que explicar isso facilita muito as coisas. E a aritmética de ponteiros são operações extremamente simples e intuitivas, se mostrado um exemplo simples com uma memória de 10 variáveis.
fonte
A explicação que eu realmente grocked foi:
fonte
Se você deseja explicar os ponteiros, deve explicar a memória primeiro. Eu costumo fazer isso usando papel milimetrado / papel quadriculado com linhas e colunas. Se o "aluno" entender a memória, poderá entender o que é um endereço. Se você tem endereço, você tem ponteiros.
Você pode brincar com essa abstração. Por exemplo, escreva o endereço (número) de um quadrado em outro quadrado. Agora desenhe uma seta do quadrado do ponteiro para o quadrado de destino. Agora substitua o ponteiro (por exemplo, aumente-o) e ajuste a seta. Escreva um endereço em outro quadrado e deixe o aluno desenhar a flecha ...
Próximo passo: dê um nome a certos quadrados (como o ponteiro). Agora você pode explicar a desreferenciação. É isso aí.
fonte
Talvez seja só eu, mas trabalho bem com analogias. Então, digamos que você tenha um amigo (função / classe) "Foo", que queira ter outra pessoa (função / classe diferente), "Bar", entre em contato com você por algum motivo. "Foo" pode levá-lo a ver "Bar", mas isso não é conveniente, movendo todos esses seres (instâncias). No entanto, "Foo" pode enviar "Bar" ao seu número de telefone (ponteiro). Dessa forma, não importa onde você esteja, "Foo" sabe como entrar em contato com você sem precisar encontrá-lo.
E, digamos que "Bar" tenha um conhecido, "Baz", que também quer entrar em contato com você. Mas você é protetor do seu número de telefone e não quer que todos tenham, "Baz" pode ligar para "Bar" (telefone como ponteiro), que pode encaminhar a chamada para você (outro ponteiro). E assim por diante na cadeia de amigos de "Baz" e amigos de amigos.
fonte
Os ponteiros fazem muito mais sentido se você estudou a linguagem assembly e / ou a arquitetura do computador. Penso que se eu ministrasse uma aula em C, começaria com algumas semanas de arquitetura para explicar o modelo de memória e os tipos de instruções que o processador realmente executa.
fonte
Meu "aha!" momento chegou neste tutorial:
Um tutorial sobre ponteiros e matrizes em C
Para ser exato, veio neste capítulo: Capítulo 3: Ponteiros e seqüências de caracteres
Para ser ainda mais preciso, veio com esta frase:
Quando li isso, as nuvens se abriram e os anjos tocaram trombetas.
Pois, veja bem, todos os tutoriais ou livros em C que eu li antes afirmaram que C poderia passar por valor ou por referência, uma mentira nefasta. A verdade é que C sempre passa por valor, mas às vezes o valor passado passa a ser um endereço. Dentro do método, é feita uma cópia desse endereço, assim como uma cópia de um int passado. Uma cópia não é feita do valor para o qual o ponteiro aponta. Assim, usando o ponteiro dentro do método, você pode acessar o valor original e alterá-lo.
Eu nunca me tornei um programador C, mas me tornei um programador .NET, e objetos e referências a objetos funcionam da mesma maneira; a referência ao objeto é passada por valor (e, portanto, copiada), mas o próprio objeto não é copiado. Eu trabalhei com muitos programadores que não entendem isso porque nunca aprenderam dicas.
fonte
O truque é explicar que a localização de uma coisa e a própria coisa não são as mesmas, assim como a imagem de um cano não é um cano . Quando você move uma coisa, sua localização muda. A localização permanece e outra coisa pode ser colocada lá.
fonte
Um ponteiro é uma nota adesiva que informa onde algo é útil. Ele contém a localização da coisa e informa o tamanho da coisa (em C, pelo menos). Portanto, um ponteiro duplo é como uma nota que diz "Há um pacote de seis na geladeira". Você precisa pegar o pacote de seis para descobrir se é Coca-Cola ou Budweiser.
fonte
Dado o código:
int v=42;
// declarando e inicializando uma variável simplesint *p = &v;
// criando um ponto p para a variável vPode-se dizer o seguinte sobre o código acima:
int * p
// "int pointer p" ... é assim que você declara um ponteiro para a variável do tipo int*p
// "apontado por p" ... são os dados para os quais p aponta, é o mesmo que v.&v
// "endereço da variável v" ... isso representa o valor literal de pfonte
http://cslibrary.stanford.edu/
Este site possui ótimos tutoriais para aprender ponteiros e gerenciamento de memória.
Eu sugiro que você percorra os fundamentos dos ponteiros, os ponteiros e a memória fornecidos no site. Você também pode examinar os problemas das listas vinculadas fornecidos no link para fortalecer ainda mais os conceitos do ponteiro.
fonte
A chave para explicar os indicadores é garantir que as pessoas para as quais você está explicando já tenham um entendimento do conceito de memória. Embora seria bom se eles realmente entendessem o nível baixo, acreditar que a memória existe como uma matriz massiva e entender que você pode acessar qualquer posição na matriz pela localização do índice é suficiente.
A próxima etapa, ter o conceito de passar o local do índice em vez de copiar toda a memória faz sentido para a maioria das pessoas. E isso é suficiente para permitir que a maioria das pessoas entenda por que os ponteiros são úteis.
a etapa final para entender os ponteiros é explicar como você pode passar como parâmetro um local de índice de memória para o método para armazenar o local de índice para onde todos os dados estão armazenados. Eu descobri que isso pode ser um passo longe demais para algumas pessoas.
Depois que alguém entende essas etapas básicas, é fácil entender que você pode encadear ponteiros indefinidamente, desde que você acompanhe quantas vezes precisa procurar endereços para encontrar o objeto de dados real.
Depois que alguém compreende os ponteiros, a próxima coisa que eles devem entender rapidamente é a diferença entre a memória heap e a pilha, e por que os ponteiros para empilhar memória são perigosos quando passados para fora do método.
fonte
Lembro-me de um livro "quebra-cabeças C" ou similar, que li puramente por ser um dos poucos livros relacionados a programação / computador disponíveis na biblioteca, meu entendimento de C era rudimentar. Ele jogou uma expressão C em você e pediu para explodir, ficando cada vez mais complicado.
fonte
Quando eu estava na universidade, meu professor tinha alguns slides em powerpoint realmente legais que descreviam um ponto como uma variável separada por si só, com uma seta para um local de memória (representado como uma matriz) e, quando estávamos fazendo listas vinculadas, ele fazia isso passo a passo, mostrando quando a seta muda, quando o ponteiro é desreferenciado, etc ..., não havia como alguém não conseguir entendê-lo em alguns minutos. O conceito em si é realmente fácil, mas fazê-lo corretamente ou aplicá-lo em programas práticos exige mais prática.
fonte
Antes de fazer isso, explico que na programação "tudo usa memória" e alocação de variável (estática) na memória. Também explicarei o que é um endereço de memória e a relação entre espaço, endereço e variáveis de memória.
Por fim, explico que existem tipos e variáveis de dados inteiros, tipos de dados e variáveis de string ... e assim por diante, até explicar que há um tipo de dados especial que armazena endereços de memória, que possui um valor vazio como 0 o "", chamado nulo .
E, finalmente, variáveis alocadas dinamicamente através do uso de ponteiros.
fonte