Ponteiros
Ponteiros é o motivo da dor de cabeça de todo estudante que está iniciando no mundo da programação, como também é algo fundamental para estruturas de dados como: Lista encadeada, lista duplamente encadeada, lista circular e árvores binárias. Apesar de em um primeiro momento parecer algo complexo na realidade seu conceito é bastante simples. Ponteiro nada mais é que um tipo especial de variável que armazena um endereço de memória, o endereço de memória, por sua vez, aponta ou referencia uma unidade de memória que armazena um valor.
Geralmente os professores explicam ponteiros usando a linguagem C, porém nesse artigo o foco será entender como ponteiros estão presentes em linguagens mais modernas como: Javascript e Python. Essas linguagens não possuem um tipo denominado ponteiro, porém existe os chamados objetos mutáveis que são arrays e objetos em Javascript ou listas e dicionários em Python.
Obeservação: Quando for mencionado objeto em Javascript ou dicionário em Python, a menção está sendo feito a uma coleção de pares chave-valor, como por exemplo:
{
"foo": 1,
"bar": 2
}
Endereço de memória
Pense no endereço de memória como o endereço da sua casa: ambos servem para localizar algo específico. Assim como existem inúmeros endereços que identificam ruas em qualquer país, também existem inúmeros endereços de memória em um computador. A quantidade de endereços disponíveis depende da arquitetura do processador. Atualmente, a maioria dos computadores utiliza uma arquitetura de 64 bits (x64), o que significa que o processador pode, teoricamente, acessar até 2⁶⁴ endereços de memória, resultando em um espaço de endereçamento total de 18 exabytes, porém, na prática o espaço de memória acessível é limitado por fatores como o hardware e sistema operacional.
Os endereços de memória são identificadores únicos para as unidades de memória. Cada unidade possui um endereço, permitindo armazenar, modificar, ler ou remover valores. Variáveis comuns contêm diretamente esses valores, enquanto ponteiros armazenam endereços de memória, permitindo o acesso indireto a esses valores. Em linguagens modernas, não existe o tipo ponteiro tampouco é possível utilizá-los de forma explicita.
Veja a ilustração a seguir:
A imagem acima ilustra os espaços na memória e os números os endereços, porém na realidade os endereços são representados por algo como 0x7ffee4b1d6a0. O tamanho de cada espaço ou unidade de memória depende da arquitetura do processador, em arquiteturas de 64 bits (x64) o tamanho de uma unidade de memória é de 1 byte (8 bits). Na imagem o espaço identificado com o índice "0" armazena o carácter "a", cada carácter utiliza 1 byte de memória, portanto a variável foo só ocupa uma unidade de memória.
Observação: Dizer que um carácter utiliza 1 byte de memória é verdade para algumas linguagens mas não para Javascript e Python, pois Javascript armazena caracteres em UTF-16, o que significa que cada caractere pode ocupar 2 bytes, enquanto Python suporta codificação Unicode o que faz com que o tamanho de um carácter seja maior que 1 byte.
Ponteiros e arrays
Array é uma estrutura de dados que armazenam uma coleção de valores, onde cada valor é identificado por um índice numérico, o tamanho não é predefinido e os valores que ele armazena podem ser constantemente modificados.
Veja a ilustração a seguir:
Talvez você esteja se perguntando qual é a relação da palavra "example" com o tópico de arrays. Bem, tem tudo a ver! Na computação, o tipo "string" não existe, na realidade o que existe é o tipo char ou carácter, uma string nada mais é do que uma coleção de caracteres ou, em termos técnicos, um array de caracteres. Em linguagens mais modernas, essa estrutura foi abstraída e transformada em um tipo próprio que é String em Javascript ou str em Python.
No entanto, algo interessante a ser notado na ilustração é que a seta aponta para o caractere "e". Isso ocorre porque variáveis não armazenam o array em si, mas sim a referência ao item inicial do array, ou seja, o que a variável realmente guarda é o endereço de memória do item localizado no índice 0. A partir dessa referência, é possível acessar os valores subsequentes do array, já que os elementos são armazenados em espaços de memória contíguos.
Mutabilidade
O conceito de mutabilidade está ligado à capacidade de modificar o conteúdo de uma variável sem precisar atribuir um novo valor a ela. Na linguagem C, é possível alterar o conteúdo de uma variável dentro de uma função, desde que ela tenha sido declarada em um escopo superior e que seu endereço de memória seja passado como parâmetro. Isso também se aplica a arrays, mas, no caso de arrays, não é necessário fornecer explicitamente o endereço de memória como parâmetro da função, pois, por padrão, passar um array como parâmetro para uma função significa passar a referência do primeiro elemento desse array.
Linguagens mais modernas, como JavaScript e Python, não possuem um tipo de ponteiro explícito como em C, mas é possível simular comportamentos semelhantes usando arrays ou listas, pois o comportamento de referência é o mesmo. Quando um array em Javascript ou uma lista em Python é passado para uma função, o que está sendo passado é a referência ao objeto (o endereço de memória do primeiro elemento). Portanto, as alterações feitas dentro da função refletem na variável declarada fora da função. Esse comportamento é conhecido como passagem por referência. Por outro lado, a passagem por valor ocorre quando objetos imutáveis, como números ou strings, são passados para funções, criando uma cópia do valor original.
O conceito de mutabilidade se aplica até mesmo a constantes. Em muitas linguagens, quando uma constante armazena um array ou lista, o que não pode ser alterado é a referência ao objeto, ou seja, a constante não pode apontar para outro array, mas o conteúdo do array em si pode ser modificado.
Hash Table
Uma tabela hash (hash table) é uma estrutura de dados que armazena pares de chave-valor e permite o acesso rápido aos valores a partir de suas chaves. De maneira resumida, uma tabela hash precisa de uma função de hash (hash function), que transforma as chaves em um valor numérico (chamado de hash) e um array, onde os valores serão armazenados. A função de hash recebe um valor fixo, como uma string ou um número e retorna um número que será o índice onde o valor será armazenado.
Os objetos em Javascript e os dicionários em Python são implementados usando hash table. Essas estruturas de dados também são mutáveis, portanto passar elas como parâmetro significa estar passando a referência do item inicial.
Conclusão
Ao longo deste artigo, foram explorados conceitos fundamentais da programação, como ponteiros, arrays, mutabilidade e tabelas hash, e como eles se manifestam em linguagens de programação modernas como JavaScript e Python. Embora essas linguagens não tenham ponteiros explícitos como em C, o comportamento de referência é simulado por meio de objetos mutáveis, como arrays, listas e dicionários. Compreender esses tópicos não apenas facilita o aprendizado de novas linguagens, mas também melhora a capacidade de resolver problemas de forma eficiente e escrever códigos com mais qualidade e robustez.