Nesse tutorial de C de nossa apostila vamos ensinar como declarar
ponteiros, fazê-los apontarem para alguma variável ou vetor, e manipulá-los.
- Download de nossa apostila: Apostila de C
Como declarar ponteiros em C
Para declarar um ponteiro, ou apontador, em C basta
colocarmos um asterisco - * - antes do nome desse ponteiro.
Sintaxe:
tipo *nome_do_ponteiro;
Por exemplo:
int *ponteiro_pra_inteiro;
float *ponteiro_pra_float;
char *ponteiro_pra_char;
Na verdade, esse asterisco pode ser encostado no tipo ou
entre o tipo e o nome.
Aqui, se você estiver com os conceitos de ponteiro na
cabeça, pode surgir uma pergunta.
“Se os ponteiros são tipos que armazenam endereço, e endereço
são apenas números, por quê ter que declarar ponteiros com os tipos (int,
float, char etc) ?”
A resposta é dada no artigo passado, em que falamos sobre
o tamanho que as variáveis ocupam em memória.
Vimos que as variáveis ocupam posições vizinhas e
contíguas (em seqüência) de memória (exceto, claro, o tipo char, que ocua só 1
byte, ou seja, só um bloco).
Vamos pegar o exemplo da variável inteira. Em minha
máquina, ela ocupa 4 bytes.
Ou seja, 4 blocos de memória, cada bloco com um endereço.
Mas o ponteiro armazena apenas um endereço de memória, e não 4.
Então, o ponteiro irá
sempre armazenar o endereço do primeiro bloco, do primeiro byte.
E os outros? Ué, se o C sabe quantos bytes cada variável
ocupa, que elas são blocos vizinhos de memória e o ponteiro sabe o endereço do
primeiro bloco, ele vai saber dos outros também!
É por isso que precisamos dizer o tipo de variável, antes
de declarar o ponteiro.
Se for um ponteiro de inteiro, estamos dizendo: “Ponteiro,
guarde esse endereço e os próximos 3, pois o inteiro tem 4 bloco”.
Se for um double: “Ponteiro, armazene o primeiro
endereço, e saiba que os próximos 7 blocos são dessa mesma variável.”
Ponteiros e Vetores em C
Já explicamos sobre a relação dos ponteiros com os diversos tipos de blocos de memória, de cada variável.
E a relação dos ponteiros com os vetores, que possuem diversas variáveis?
Pois bem, eles têm (ponteiros e vetores) possuem uma relação especial.
Quando declaramos um vetor, estamos declarando um conjunto de variáveis também contíguas, e cada uma dessas variáveis ocupam vários bytes (ou só 1 byte, se for char). Então, um vetor é um conjunto maior ainda de bytes, de blocos de memória.
Como você sabe, quando apontamos um ponteiro para uma variável, esse ponteiro armazena o endereço do primeiro byte, do menor endereço, da variável.
A relação com vetores é análoga: o nome do vetor é, na verdade, o endereço do primeiro elemento desse vetor.
Ou seja, se declararmos um vetor de nome vetor, não importando o número de elementos, se imprimirmos o nome vetor dentro de um printf, veremos o endereço da primeira variável daquele vetor.
Podemos ver um vetor como um ponteiro.
Isso explica o fato de que quando passamos um vetor para uma função, essa função altera de fato o valor do vetor. Isso ocorre pois não estamos passando uma cópia do vetor (como acontece com as variáveis).
Isso ocorre porque quando passamos o nome do vetor, estamos passando um ponteiro pra função.
Ou seja, estamos passando um endereço, onde a função vai atuar.
E endereço de memória é o mesmo, dentro ou fora de uma função.
Rode o seguinte exemplo para se certificar do que foi ensinado aqui.
Ou seja, para declararmos um ponteiro ptr para um vetor vet[ ], fazemos:
ptr = vet;
Pois o nome do vetor é um ponteiro (que não muda) para o primeiro elemento.
Então poderíamos fazer assim também:
ptr = &vet[0];
Rode o seguinte exemplo para se certificar do que foi ensinado aqui.
Exemplo de código em C
Crie um programa que mostre que o nome de um vetor é, na verdade, um ponteiro para a primeira posição que o vetor ocupa na memória. Ou seja, um vetor sempre aponta para o elemento 0.#include <stdio.h> // Curso C Progressivo: www.cprogessivo.net // O melhor curso de C! Online e gratuito ! // Artigos, apostilas, tutoriais e vídeo-aulas sobre // a linguagem de programação C !int main(void){ int teste[10]; printf("Imprimindo o vetor 'teste': %d\n", teste); printf("Imprimindo o endereço do primeiro elemento: %d\n", &teste[0]);
return 0;
}
Ou seja, para declararmos um ponteiro ptr para um vetor vet[ ], fazemos:
ptr = vet;
Pois o nome do vetor é um ponteiro (que não muda) para o primeiro elemento.
Então poderíamos fazer assim também:
ptr = &vet[0];
Como inicializar um ponteiro em C – A constante NULL
Já vimos como declarar um ponteiro, então é hora de fazer
com que eles cumpram sua missão.
Vamos fazer os ponteiros apontarem.
Lembra que ensinamos como checar o endereço de uma variável
ou vetor apenas usando o símbolo & antes da variável?
Agora vamos fazer isso novamente, mas é para pegar esse
endereço e armazenar em um ponteiro.
Por exemplo, se quisermos armazenar o endereço do inteiro
‘numero’ no ponteiro ‘numeroPtr’, fazemos:
int numero = 5;
int *numeroPtr = №
Pronto, agora nosso ponteiro está apontando para a
variável numero, pois o ponteiro
guardou o endereço do inteiro na sua posição de memória.
Muito cuidado! Ponteiros
armazenam endereços, e não valores. Ou seja, se fizer:
int *numeroPtr = numero;
Estará comentendo um erro!
É sempre bom inicializarmos os ponteiros, pois senão eles
podem vir com lixo e você se esquecer, posteriormente, de inicializar. Então,
quando for usar, pensará que está usando o ponteiro de modo correto, mas estará
usando o ponteiro com ele apontando para um lixo (endereço qualquer de
memória).
Uma boa prática é apontar os ponteiros para a primeira
posição de memória, que é conhecida como NULL.
Sempre que terminar de usar um ponteiro, coloque ele pra apontar para a posição
NULL. Para fazer isso, faça:
tipo *nome_do_ponteiro = NULL;
Exemplo de código:
Como usar ponteiros
Crie um programa em C que declara um inteiro e uma
variável do tipo double. Em seguida, crie dois ponteiros apontando para essas
variáveis e mostre o endereço de memória das variáveis, e mostre o endereço de
memória que cada ponteiro armazenou.
Por fim, coloque esses ponteiros para a primeira posição
(NULL), de memória.
Para saber o endereço de uma variável dentro do printf,
colocamos o %d e depois ‘&nome_variavel’.
Para saber que endereço um ponteiro armazena no printf,
também colocamos o %d entre as aspas, e fora colocamos apenas o nome do
ponteiro.
Veja como ficou nosso código sobre como fazer esse programa em C:
#include <stdio.h> // Curso C Progressivo: www.cprogessivo.net // O melhor curso de C! Online e gratuito ! // Artigos, apostilas, tutoriais e vídeo-aulas sobre // a linguagem de programação C !int main(void){ int inteiro; int *inteiro_ptr = &inteiro; double double1; double *double_ptr = &double1; printf("Endereco da variariavel 'inteiro': %d\n", &inteiro); printf("Endereco armazenado no ponteiro 'inteiro_ptr': %d\n\n", inteiro_ptr); printf("Endereco da variariavel 'double1': %d\n", &double1); printf("Endereco armazenado no ponteiro 'double_ptr': %d\n\n", double_ptr); printf("Apos o uso dos ponteiros, vamos aponta-los para NULL\n\n"); inteiro_ptr = NULL; double_ptr = NULL; printf("Endereco armazenado no ponteiro 'inteiro_ptr': %d\n", inteiro_ptr); printf("Endereco armazenado no ponteiro 'double_ptr': %d\n", double_ptr);
return 0; }
15 comentários:
Apostila C Progressivo; É a melhor que existe!
Em nome de todos os leitores; Meus Parabéns!
Quem só usou ctrl c + ctrl v ta com 'variariavel' no printf. Além de que provavelmente não aprendeu direito. Aqui to adorando o Curso Obrigado e Parabéns. estudando e praticando muito.
#include
// Curso C Progressivo: www.cprogessivo.net
// O melhor curso de C! Online e gratuito !
// Artigos, apostilas, tutoriais e vídeo-aulas sobre
// a linguagem de programação C !
int main(void)
{
int teste[10];
printf("Imprimindo o vetor 'teste': %d\n", teste);
printf("Imprimindo o endereço do primeiro elemento: %d\n", &teste[0]);
return 0;
}
na apostila anterior aconteceu a mesma coisa. o programa nao é compilado pq aparece a mensagem: linha 13 aviso: formato '%d' espera argumento do tipo 'int', orem o argumento 2 possui tipo 'int *' [-wformat]....
pelo que eu entendi o compilador nao esta tratando o numero do endereço como um numero inteiro por isso nao esta aceitando '%d'...
é isso mesmo???
André de Souza, sim. %d realmente é para inteiros. Para ponteiros, use %p.
Ainda nao entendi a importancia de saber os endereços das variaveis
"Blogger Roberto GMJ disse...
Quem só usou ctrl c + ctrl v ta com 'variariavel' no printf. Além de que provavelmente não aprendeu direito. Aqui to adorando o Curso Obrigado e Parabéns. estudando e praticando muito."
Que eu saiba aquilo era um exemplo , não uma questão....
Excelente explicação, finalmente estou começando a entender ponteiros!
Muito obrigado equipe do C progressivo!
Excelente trabalho, está fácil aprender isso!
Não consegui entender claramente a necessidade de apontar para NULL. Se depois eu precisar utilizar o ponteiro novamente, vou ter que declarar ele para o endereço da variável novamente? Se alguem puder esclarecer melhor esse conceito de apontar para NULL, eu agradeço.
Há duas formas mais utilizadas de se imprimir o endereço de memória. Podemos usar o %d, que foi utilizado no post, e seria o endereço de memória na base 10. Ou também podemos utilizar o %p, que nos retorna o endereço de memória na base hexadecimal. Tenho costume de utilizar %p, mas acredito que no final das contas não faça nenhuma diferença.
Ótimo post, obrigado por manter o site em funcionamento!
Olá
Cheguei aqui porque estava pesquisando o tema da inicialização de um ponteiro. Tive essa dúvida ao tentar repetir os passos de um programador da web que fez um programinha para console e usou ponteiro, mas o fez dessa forma:
const char *titulo = "MENU PARA DIVIDIR";
const char *opciones[] = {"Dividir enteros","Dividir flotantes","Regressar"};
int n = 3
opcion= menu(titulo, opciones, n);
Ou seja, ele deu valores para os ponteiros e não endereços. Considerando o texto desta parte da apostila fica a dúvida do que esse programador exatamente fez?
Ola joagostini.
Neste caso os valores passados sao enderecos, pois o primeiro recebe uma sequencia de caracteres (um vetor, que automaticamente retorna um endereco inteiro) e o segundo eh um vetor que contem vetores, o que tambem retorna um endereco inteiro.
mas que belo curso! muito bom!
Antes, preciso agradecer a vocês do cprogressivo.net pelas publicações que ajudam a todos nós!
Mas quero informar um equívoco na publicação:
Onde: "apontar os ponteiros para a primeira posição de memória, que é conhecida como NULL"
Isso não é verdade!
NULL, não implica em qualquer significado físico ou lógico de localização na memória.
Embora NULL seja frequentemente representado como zero, isso não implica que ele realmente aponta para o endereço de memória física zero. Na maioria dos sistemas operacionais modernos, a tentativa de acessar o endereço de memória zero (ou qualquer endereço associado a NULL) causará uma violação de acesso (segmentation fault), porque esse endereço é normalmente protegido ou reservado pelo sistema. O endereço zero na memória é geralmente não utilizável por programas de usuário e é reservado para sinalizar endereços inválidos.
A conversão de ponteiros nulos para o valor zero é uma convenção
Postar um comentário