# Programar em C/Ponteiros

Poderíamos escrever um livro inteiro sobre ponteiros, pois o conteúdo é demasiadamente extenso. Por esse motivo este assunto foi dividido em básico, intermediário e avançado, assim o leitor poderá fazer seus estudos conforme suas necessidades.
É recomendável para quem está vendo programação pela primeira vez aqui que não se preocupe com o avançado sobre ponteiros por enquanto.

## Básico
### O que é um ponteiro?

Um ponteiro é simplesmente uma variável que armazena o endereço de outra variável.
Um exemplo : O que é o ponteiro de um relógio? É o que aponta para as horas, minutos ou segundos. Um ponteiro aponta para algo. Em programação, temos as variáveis armazenadas na memória, e um ponteiro aponta para um endereço de memória.
Imagine as variáveis como documentos, a memória do computador como pastas para guardar os documentos, e o ponteiro como atalhos para as pastas.
Não se desespere caso não consiga entender num primeiro momento, o conceito fica mais claro com a prática.

### Declarando e acessando ponteiros

Um ponteiro, como qualquer variável, deve ter um tipo, que é o tipo da variável para a qual ele aponta. Para declarar um ponteiro, especificamos o tipo da variável para a qual ele aponta e seu nome precedido por asterisco:
Tome cuidado ao declarar vários ponteiros em uma linha, pois o asterisco deve vir antes de cada nome de variável. Note os três exemplos:
Para acessar o endereço de uma variável, utilizamos o operador & (E comercial), chamado "operador de referência" ou "operador de endereço". Como o nome sugere, ele retorna o endereço na memória de seu operando. Ele é unário e deve ser escrito antes do seu operando. Por exemplo, se uma variável nome foi guardada no endereço de memória 1000, a expressão &nome valerá 1000.
Com isso, fica claro o esquema ao lado: a variável a contém o valor 1234 e o ponteiro p contem o endereço de a (&a).
Para atribuir um valor ao ponteiro, usamos apenas seu nome de variável. Esse valor deve ser um endereço de memória, portanto obtido com o operador &:
Claro que também podemos inicializar um ponteiro:
Nos dois casos, o ponteiro p irá apontar para a variável a.
Mas, como o ponteiro contém um endereço, podemos também atribuir um valor à variável guardada nesse endereço, ou seja, à variável apontada pelo ponteiro. Para isso, usamos o operador * (asterisco), que basicamente significa "o valor apontado por". Ex:
Para ver o resultado :
Cuidado! Você nunca deve usar um ponteiro sem antes inicializá-lo; esse é um erro comum. Inicialmente, um ponteiro pode apontar para qualquer lugar da memória do computador. Ou seja, ao tentar ler ou gravar o valor apontado por ele, você estará manipulando um lugar desconhecido na memória!
Nesse exemplo, estamos a manipular um lugar desconhecido da memória! Se você tentar compilar esse código, o compilador deverá dar uma mensagem de aviso; durante a execução, provavelmente ocorrerá uma falha de segmentação (erro que ocorre quando um programa tenta acessar a memória alheia).
Um exemplo mais elaborado:
Primeiramente declaramos a variável i, com valor 10, e o ponteiro p, que apontará para o endereço de i. Depois, guardamos o valor 5 no endereço apontado por p. Se você executar esse exemplo, verá algo parecido com:
É claro que os valores de i e de *p serão iguais, já que p aponta para i. O terceiro valor é o endereço de memória onde está i (e, consequentemente, é o próprio valor de p), e será diferente em cada sistema.
Cuidado! Os operadores unários & e * não podem ser confundidos com os operadores binários AND bit a bit e multiplicação, respectivamente.

### Ponteiro e NULL

Uma falha de segmentação ou em inglês (segmentation fault) ocorre quando um programa tenta acessar um endereço na memória que está reservado ou que não existe.Nos sistemas Unix quando acontece este tipo de erro o sinal SIGSEGV é enviado ao programa indicando uma falha de segmentação.
Aqui o ponteiro contem null, definido com o endereço (0x00000000) que causa uma falha de segmentação .
Esse programa termina anormalmente. Você esta tentando colocar o valor 5 em um endereço inválido.
Para que isso não aconteça o ponteiro deve ser inicializado com um endereço valido. Exemplo :
NULL está definido dentro do cabeçalho stddef.h . Aqui você não espera que o programa acabe com algum tipo de mágica, se NULL é igual ao valor do ponteiro isso significa que não foi encontrado nem um endereço acessível, então você para. Caso contrario você estará executando uma operação que não é permitida. Ou colocar 5 em (0x00000000) .

### Mais operações com ponteiros

Suponhamos dois ponteiros inicializados p1 e p2. Podemos fazer dois tipos de atribuição entre eles:
Esse primeiro exemplo fará com que p1 aponte para o mesmo lugar que p2. Ou seja, usar p1 será equivalente a usar p2 após essa atribuição.
Nesse segundo caso, estamos a igualar os valores apontados pelos dois ponteiros: alteraremos o valor apontado por p1 para o valor apontado por p2.
Agora vamos dar mais alguns exemplos com o ponteiro p:
Aqui estamos a incrementar o ponteiro. Quando incrementamos um ponteiro ele passa a apontar para o próximo valor do mesmo tipo em relação ao valor para o qual o ponteiro aponta. Isto é, se temos um ponteiro para um inteiro e o incrementamos, ele passa a apontar para o próximo inteiro. Note que o incremento não ocorre byte-a-byte!
Aqui, colocamos *p entre parênteses para especificar que queremos alterar o valor apontado por p. Ou seja, aqui iremos incrementar o conteúdo da variável apontada pelo ponteiro p.
Neste caso, o efeito não é tão claro quanto nos outros exemplos. A precedência do operador ++ sobre o operador * faz com que a expressão seja equivalente a (*p)++. O valor atual de p é retornado ao operador *, e o valor de p é incrementado. Ou seja, obtemos o valor atual do ponteiro e já o fazemos apontar para o próximo valor.
Esta linha atribui a uma variável x o conteúdo do décimo-quinto inteiro adiante daquele apontado por p. Por exemplo, suponhamos que tivéssemos uma série de variáveis i0, i1, i2, … i15 e que p apontasse para i0. Nossa variável x receberia o valor de i15.
Tente acompanhar este exemplo dos dois tipos de atribuição de ponteiros:

## Intermediário
### Ponteiro de estrutura

Para começar e deixar mais claro definimos uma estrutura simples com dois campos.
O passo seguinte é definir um ponteiro para essa estrutura.
A partir do ponteiro podemos ter acesso a um campo da estrutura usando um seletor "->" (uma flecha).
O mesmo resultado pode ser obtido da seguinte forma.
O operador cast também e bastante utilizado para estruturar áreas de estoque temporários (buffer). Os tipos dentro da estrutura devem ser o mesmo do arranjo para evitar problemas de alinhamento.
A seguir um pequeno exemplo:

### Ponteiros como parâmetros de funções

Comecemos por uma situação-problema: eu tenho 2 variáveis e quero trocar o valor delas. Vamos começar com um algoritmo simples, dentro da função main:
Esse exemplo funcionará exatamente como esperado: primeiramente ele imprimirá "5 10" e depois ele imprimirá "10 5". Mas e se quisermos trocar várias vezes o valor de duas variáveis? É muito mais conveniente criar uma função que faça isso. Vamos fazer uma tentativa de implementação da função swap (troca, em inglês):
No entanto, o que queremos não irá acontecer. Você verá que o programa imprime duas vezes "5 10". Por que isso acontece? Lembre-se do escopo das variáveis: as variáveis a e b são locais à função main, e quando as passamos como argumentos para swap, seus valores são copiados e passam a ser chamados de i e j; a troca ocorre entre i e j, de modo que quando voltamos à função main nada mudou.
Então como poderíamos fazer isso? Como são retornados dois valores, não podemos usar o valor de retorno de uma função. Mas existe uma alternativa: os ponteiros!
Neste exemplo, definimos a função swap como uma função que toma como argumentos dois ponteiros para inteiros; a função faz a troca entre os valores apontados pelos ponteiros. Já na função main, passamos os endereços das variáveis para a função swap, de modo que a função swap possa modificar variáveis locais de outra função. O único possível inconveniente é que, quando usarmos a função, teremos de lembrar de colocar um & na frente das variáveis que estivermos passando para a função.
Se você pensar bem, já vimos uma função em que passamos os argumentos precedidos de &: é a função scanf! Por que fazemos isso? É simples: chamamos a função scanf para que ela ponha nas nossas variáveis valores digitados pelo usuário. Ora, essas variáveis são locais, e portanto só podem ser alteradas por outras funções através de ponteiros!
Quando uma função recebe como parâmetros os endereços e não os valores das variáveis, dizemos que estamos a fazer uma chamada por referência; é o caso desse último exemplo. Quando passamos diretamente os valores das variáveis para uma função, dizemos que é uma chamada por valor; foi o caso do segundo exemplo. Veja mais um exemplo abaixo:

### Ponteiros e vetores

Em C, os elementos de um vetor são sempre guardados sequencialmente, a uma distância fixa um do outro. Com isso, é possível facilmente passar de um elemento a outro, percorrendo sempre uma mesma distância para frente ou para trás na memória. Dessa maneira, podemos usar ponteiros e a aritmética de ponteiros para percorrer vetores. Na verdade, vetores são ponteiros ― um uso particular dos ponteiros. Acompanhe o exemplo a seguir.
Começamos declarando um vetor com três elementos; depois, criamos um ponteiro para esse vetor. Mas repare que não colocamos o operador de endereço em vetorTeste; fazemos isso porque um vetor já representa um endereço, como você pode verificar pelo resultado da primeira chamada a printf.
Como você já viu anteriormente neste capítulo, podemos usar a sintaxe *(ptr + 1) para acessar o inteiro seguinte ao apontado pelo ponteiro ptr. Mas, se o ponteiro aponta para o vetor, o próximo inteiro na memória será o próximo elemento do vetor! De fato, em C as duas formas *(ptr + n) e ptr[n] são equivalentes.
Não é necessário criar um ponteiro para usar essa sintaxe; como já vimos, o vetor em si já é um ponteiro, de modo que qualquer operação com ptr será feita igualmente com vetorTeste. Todas as formas abaixo de acessar o segundo elemento do vetor são equivalentes:
Veja mais este exemplo:
Ele resume as várias formas de acessar elementos de um vetor usando ponteiros.

### Indexação estranha de ponteiros

o C permite fazer um tipo indexação de um vetor quando uma variável controla seu índice. O seguinte código é válido e funciona: Observe a indexação vetor[i].
Essa indexação, apesar de estranha, funciona corretamente e sem aviso na compilação. Ela é prática, mas, para os iniciantes, pode parecer complicada. É só treinar para entender.

### Comparando endereços

Como os endereços são números, eles também podem ser comparados entre si. Veja o exemplo a seguir, com efeito equivalente ao primeiro exemplo da seção anterior:
Esse programa incrementa o ponteiro enquanto esse endereço for igual (ou menor) ao endereço do último elemento do vetor (lembre-se que os índices do vetor são 0, 1 e 2).

## Avançado
### Ponteiros para ponteiros

Note que um ponteiro é uma variável como outra qualquer, e por isso também ocupa espaço em memória. Para obtermos o endereço que um ponteiro ocupa em memória, usamos o operador &, assim como fazemos nas variáveis comuns.
Mas e se estivéssemos interessados em guardar o endereço de um ponteiro, que tipo de váriavel deveria recebe-lo? A resposta é: um ponteiro, isto é, um ponteiro para outro ponteiro.
Considere a seguinte declaração:
int x = 1;
Declaramos uma variável chamada x com o valor 1.
Como já sabemos, para declarar um ponteiro, deve-se verificar o tipo da variável que ele irá apontar (neste caso int) e colocar um asterisco entre o tipo da variável e o nome do ponteiro:
int * p_x = &x;
Declaramos um ponteiro apontado para x.
Agora, para se guardar o endereço de um ponteiro, os mesmos passos devem ser seguidos. Primeiramente verificamos os tipo da variável que será apontada (int *) e colocamos um asterisco entre o tipo e nome do ponteiro:
int ** p_p_x = &p_x;
Declaramos um ponteiro que irá apontar para o ponteiro p_x, ou seja, um ponteiro para ponteiro. Note que C não impõe limites para o número de asteriscos em uma variável.
No exemplo a seguir, todos os printf irão escrever a mesma coisa na tela.
Percebe que **p_p_x consiste no valor da variável apontada pelo endereço apontado por p_p_x.
Uma aplicação de ponteiros para ponteiros está nas strings, já que strings são vetores, que por sua vez são ponteiros. Um vetor de strings seria justamente um ponteiro para um ponteiro.

### Passando vetores como argumentos de funções

Os ponteiros podem ser passados como argumentos de funções.
Parâmetro ponteiro passando um array.
Repare que passamos dois parâmetros para as funções:
* O "nome" do vetor, que representa o seu endereço na memória. (Temos 3 maneiras para passar o endereço do vetor: diretamente pelo seu "nome", via um ponteiro ou pelo endereço do primeiro elemento.)
* Uma constante, que representa o número de elementos do vetor. Isso é importante pois o C não guarda informações sobre o tamanho dos vetores; você não deve tentar alterar ou acessar valores que não pertencem ao vetor.
É claro que devemos passar o endereço do vetor (por "referência"), pois os seus valores são alterados pela função atribuiValores. De nada adiantaria passar o vetor por valor, pois o valor só seria alterado localmente na função (como já vimos no caso de troca do valor de duas variáveis).
Por causa dessa equivalência entre vetores e ponteiros, podemos fazer uma pequena alteração no protótipo (tanto na declaração quanto na definição) das funções atribuiValores e mostraValores, sem precisar alterar o código interno dessas funções ou a chamada a elas dentro da função main ? trocando
por
Para o compilador, você não fez mudança alguma, justamente por conta dessa equivalência. Em ambos os casos, foi passado o endereço do vetor para as funções.

### Ponteiros para funções

Os ponteiros para funções servem, geralmente, para passar uma função como argumento de uma outra função. Neste exemplo
Veja que criamos uma função que retorna a soma dos dois inteiros a ela fornecidos; no entanto, ela não é chamada diretamente. Ela é chamada pela função operacao, através de um ponteiro. A função main passa a função soma como argumento para operacao, e a função operacao chama essa função que lhe foi dada como argumento.
Note bem o terceiro argumento da função operacao: ele é um ponteiro para uma função. Nesse caso, ele foi declarado como um ponteiro para uma função que toma dois inteiros como argumentos e retorna outro inteiro. O * indica que estamos declarando um ponteiro, e não uma função. Os parênteses em torno de *func são essenciais, pois sem eles o compilador entenderia o argumento como uma função que retorna um ponteiro para um inteiro.
A forma geral para declarar um ponteiro para uma função é:
Para chamar a função apontada pelo ponteiro, há duas sintaxes. A sintaxe original é
Se ptr é um ponteiro para uma função, faz bastante sentido que a função em si seja chamada por *ptr. No entanto, a sintaxe mais moderna permite que ponteiros para funções sejam chamados exatamente da mesma maneira que funções:
Por fim, para inicializar um ponteiro para função, não precisamos usar o operador de endereço (ele já está implícito). Por isso, quando chamamos a função operacao, não precisamos escrever &soma.
Veja mais um exemplo — na verdade, uma extensão do exemplo anterior:
Aqui, criamos mais uma função, subtracao, além de criar um outro ponteiro para ela (uma espécie de "atalho"), menos. Na função main, referimo-nos à função de subtração através desse atalho.
Veja também que aqui usamos a sintaxe moderna para a chamada de ponteiros de funções, ao contrário do exemplo anterior.