Vamos agora apresentar um novo tipo dado: os ponteiros.
- Certificado de programação C
- Leia este tutorial no seu computador ou celular: Apostila C Progressivo
O que são ponteiros em C ?
Não se assuste com o tanto de tipos de variáveis, elas
são feitas, em sua maioria, para que os humanos possam entender e trabalhar
mais facilmente com os diversos tipos de dados.
Porém, para o computador, não existe praticamente
diferença alguma entre as variáveis, para ele é tudo bit, é tudo 1 ou 0.
Um meio bastante usado nos hardwares para administrar
esse número gigantesco de 1’s e 0’s, é através do endereçamento.
Cada trecho da memória tem um endereço único. Não existem
dois bits, em uma máquina, que tenha o mesmo endereço de memória.
O ato de selecionar, ou alocar, um espaço de memória em C
é feito no momento da declaração.
Endereços de memória são um tipo de dado tão importante,
ou até mais, que qualquer outro tipo de dado. Quem já trabalhou com eletrônica,
em baixo nível, sabe que endereçamento é uma parte essencial de qualquer
circuito digital. Nossos computadores possuem diversos dispositivos que são
responsáveis somente pelo endereçamento.
E é isso que o um ponteiro é: um tipo de dado que serve
para indicar, ou armazenar, um endereço de memória.
Um ponteiro não é um inteiro, é um tipo que armazena o
endereço em que o inteiro está alocado.
Um ponteiro não é um float ou double, ponteiro é um tipo
de dado que armazena o endereço em que o float ou double está.
Um ponteiro não é um char, ponteiro é um tipo de dado que
pode armazenar o endereço em que um caractere está.
É muito importante que você entenda, e se lembre bem
disso, pois é muito comum os iniciantes confundirem ponteiros com outros tipos
de dados.
Essa confusão é uma dificuldade natural que todas as
pessoas têm ao se depararem pela primeira vez com o uso dos ponteiros.
O interessante é que as pessoas não confundem inteiro com
um caractere, ou caractere com um número decimal, mas confundem ponteiro com
números inteiros.
Isso se deve ao fato dos ponteiros serem um tipo de
abstração, criado especialmente para facilitar o trabalho da computação em
baixo nível, da computação que mexe diretamente com a memória de seu computador,
poder este que pouquíssimas linguagens possuem.
Como saber o endereço de memória de uma variável: &
Sempre que declaramos uma variável e usamos ela, estamos
trabalhando com seu valor.
Por exemplo:
numero1 = 1;
numero2 = 2;
letra1 = ‘a’;
letra2 = ‘b’;
O valor da variável ‘numero1’ é 1, o valor da variável ‘numero2’
é 2, o valor da variável ‘letra1’ é ‘a’ e o valor da variável ‘letra2’ é ‘b’.
Fixe bem esse detalhe: esse é o valor que está armazenado
na memória, essas variáveis são um conjunto de bits, um conjunto de
informações, um valor.
Agora vamos descobrir em qual posição da memória esses
valores estão.
Para isso, basta colocarmos o símbolo de E comercial
antes da variável: &
Para saber o endereço da variável ‘numero1’, fazemos:
&numero1
Para saber o endereço da variável ‘numero2’, fazemos:
&numero2
Para saber o endereço da variável ‘letra1’, fazemos:
&letra1
Para saber o endereço da variável ‘letra2’, fazemos:
&letra2
Para facilitar a visualização do usuário, podemos
imaginar a memória como um vetor gigantesco de espaços, e esses espaços são numerados
com números inteiros.
Veja bem, embora seja um inteiro, não quer dizer que o valor
seja inteiro.
Todos os endereços são números inteiros, mas nem todo o
valor armazenado dentro daquele endereço de memória é inteiro.
Vamos fazer um exemplo para entender melhor a diferença
entre valor e endereço de uma memória.
Exemplo de código: Vendo o valor e endereço de uma variável
Crie um programa em C que declara dois números inteiros e
dois caracteres do tipo char (todos devidamente inicializados).
Em seguida, mostre o VALOR de cada variável, bem como seu
ENDEREÇO
Depois, altere os valores das variáveis e mostre
novamente o VALOR e ENDEREÇO de cada variável desta.
Após rodar esse exemplo, você verá a clara diferença
entre o VALOR e o ENDEREÇO de uma variável na memória de seu computador.
O valor é aquela informação que você inicia, e endereço é
um número inteiro ENORME.
O valor é aquela informação que é alterada, já o endereço
de uma variável permanece CONSTANTE!
Faz sentido, para você?
#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 !aint main(void){ int numero1=1, numero2=2; char letra1='a', letra2='b'; printf("numero1: \n"); printf("Valor: %d\n", numero1); printf("Endereco na memoria: %d\n\n", &numero1); printf("numero2: \n"); printf("Valor: %d\n", numero2); printf("Endereco na memoria: %d\n\n", &numero2); printf("letra1: \n"); printf("Valor: %c\n", letra1); printf("Endereco na memoria: %d\n\n", &letra1); printf("letra2: \n"); printf("Valor: %c\n", letra2); printf("Endereco na memoria: %d\n\n", &letra2); printf("Alterando os valores...\n\n"); numero1=2112; numero2=666; letra1='A'; letra2='B'; printf("numero1: \n"); printf("Valor: %d\n", numero1); printf("Endereco na memoria: %d\n\n", &numero1); printf("numero2: \n"); printf("Valor: %d\n", numero2); printf("Endereco na memoria: %d\n\n", &numero2); printf("letra1: \n"); printf("Valor: %c\n", letra1); printf("Endereco na memoria: %d\n\n", &letra1); printf("letra2: \n"); printf("Valor: %c\n", letra2); printf("Endereco na memoria: %d\n\n", &letra2); return 0; }
14 comentários:
Na compilação apresentou esta mensagem aviso: formato ‘%d’ espera argumento do tipo ‘int’, porém o argumento 2 possui tipo ‘int *’ [-Wformat] . Então nas linhas (ex) printf("Endereco na memoria: %p\n\n", &numero1); troquei '%d' por '%p'
Muito bom o curso. Estou gostando bastante! Parabéns pela iniciativa e sucesso!
Um sucesso essa pagina!
Olá ! Aprimorei o código anterior:
// Ponteiros: incremento / decremento
#include < stdio.h >
#include < string.h >
#include < ctype.h >
#define DIM 8
#define DIM2 9
int a=10,b=23;
int *ptr_x = NULL;
void preenche_com_end_mem(int end_var, char j[DIM]); // protótipo de função
void mostra_pointer(int *ptr_x, char j, int end_ptr);
void tecle_anything(); // protótipo de função
main()
{
while(1)
{
system("cls || clear");
printf("PONTEIRO: variavel que armazena endereco de memoria de outra variavel.\n");
printf("INCREMENTO / DECREMENTO de Ponteiros:\n\n");
printf("Declaracoes de variaveis feitas nesse programa:\n");
printf("int a=%d, b=%d;\n",a,b);
printf("int *ptr_x = NULL;\n\n");
printf("Endereco de memoria da variavel (a) : &a = %d\n", &a);
printf("Endereco de memoria da variavel (b) : &b = %d\n", &b);
printf("Endereco de memoria do ponteiro (ptr_x): &ptr_x = %d\n\n", &ptr_x);
printf("Como o tipo (int) tem %d bytes de tamanho:\n",sizeof(int));
preenche_com_end_mem((int)&a,"(a)");
preenche_com_end_mem((int)&b,"(b)");
preenche_com_end_mem((int)&ptr_x, "(ptr_x)");
printf("\n\n");
tecle_anything();
printf("\rE agora, escrevendo: ptr_x = &a; ... como fica ?\n");
ptr_x = &a;
tecle_anything();
mostra_pointer(ptr_x, 'a',(int) &ptr_x);
tecle_anything();
printf("\rINCREMENTO de Ponteiro: instrucao ptr_x++; para onde apontara ptr_x agora?\n");
ptr_x++;
tecle_anything();
mostra_pointer(ptr_x, 'b',(int) &ptr_x);
tecle_anything();
printf("\rDECREMENTO de Ponteiro: instrucao ptr_x--; para onde apontara ptr_x agora?\n");
ptr_x--;
tecle_anything();
mostra_pointer(ptr_x, 'b',(int) &ptr_x);
system("pause");
}
}
// FUNÇÃO que mostra os endereços de memória usados por uma variável
void preenche_com_end_mem(int end_var, char j[DIM])
{
int v[sizeof(int)], i;
char tmp[DIM2];
v[0]=end_var;
for(i=1;i<=sizeof(int);i++)
{v[i]=v[i-1]+1;}
if (strcmp(j,"(ptr_x)")!=0)
strcpy(tmp,"Variavel");
else
strcpy(tmp,"Ponteiro");
printf("%s %-7s ocupa os enderecos: ",tmp,j);
for(i=0;i<sizeof(int);i++)
{printf("|%d| ",v[i]);}
printf("\n");
}
// FUNÇÃO Mostra as informações do Ponteiro
void mostra_pointer(int *ptr_x, char j, int end_ptr)
{
printf("\rConteudo do Ponteiro = endereco de memoria da variavel (%c): prt_x = %d\n", j, ptr_x);
printf("Conteudo da variavel apontada = variavel (%c): *prt_x = %d\n", j, *ptr_x);
printf("O Ponteiro tambem tem o seu proprio endereco: &prt_x = %d\n\n", end_ptr);
}
// FUNÇÃO
void tecle_anything()
{
printf("Digite qualquer tecla para continuar...");getch();
}
Olá ! Rotina básica sobre ponteiros:
// apresentação de Ponteiros
#include < stdio.h >
int a;
int *ptr_a = NULL;
void tecle_anything(); // protótipo da função
main()
{
while(1)
{
system("cls || clear");
printf("PONTEIRO: variavel que armazena endereco de memoria de outra variavel.\n");
printf("BASICO:\n\n");
printf("Declaracoes de variaveis feitas nesse programa:\n");
printf("int a;\n");
printf("int *ptr_a = NULL;\n\n");
printf("Vamos atribuir um valor para a variavel (a): ");scanf("%d", &a);
printf("Variavel (a): %d\n", a);
printf("Endereco de memoria da variavel (a): &a = %d\n", &a);
tecle_anything();
printf("\rE agora, escrevendo: ptr_a = &a; ... como fica ?\n");
ptr_a = &a;
tecle_anything();
printf("\rConteudo do Ponteiro = endereco de memoria da variavel (a): prt_a = %d\n", ptr_a);
printf("Conteudo da variavel apontada = variavel (a): *prt_a = %d\n", *ptr_a);
printf("O Ponteiro tambem tem o seu proprio endereco: &prt_a = %d\n\n", &ptr_a);
tecle_anything();
printf("\rAlterando o valor da variavel (a), atraves do Ponteiro: digite um valor: "); scanf("%d", ptr_a);
printf("Observe que tudo fica igual, menos o valor da variavel (a) e *prt_a:\n");
tecle_anything();
printf("\rAplicando scanf(\"%%d\", ptr_a); ou a instrucao *ptr_a = %d, obtemos:\n", *ptr_a);
printf("Variavel (a): %d\n", a);
printf("Endereco de memoria da variavel (a): &a = %d\n", &a);
printf("Conteudo do Ponteiro = endereco de memoria da variavel (a): prt_a = %d\n", ptr_a);
printf("Conteudo da variavel apontada = variavel (a): *prt_a = %d\n", *ptr_a);
printf("O Ponteiro tambem tem o seu proprio endereco: &prt_a = %d\n\n\n", &ptr_a);
system("pause");
}
}
// função tecle_anything
void tecle_anything()
{
printf("Digite qualquer tecla para continuar...");getch();
}
Olá ! Segue abaixo um programa simples, mas que ajuda a entender os ponteiros de ponteiros:
// Ponteiros de Ponteiros
#include < stdio.h >
int a;
int *ptr_a1 = NULL; // declaração do ponteiro da variável (a)
int **ptr_a2 = NULL; // declaração do ponteiro para o ponteiro da variável (a)
int ***ptr_a3 = NULL; // declaração do ponteiro para o ponteiro para o ponteiro da variável (a)
void tecle_anything(); // protótipo da função
main()
{
while(1)
{
system("cls || clear");
printf("PONTEIROS DE PONTEIROS: 3 niveis de Ponteiros\n");
printf("Observe como os (*) mudam o conteudo apontado pelos Ponteiros.\n");
printf("Nao ha um limite pre-determinado para o uso de niveis, atraves dos (*).\n\n");
printf("Declaracoes de variaveis feitas nesse programa:\n");
printf("int a;\n");
printf("int *ptr_a1 = NULL;\n");
printf("int **ptr_a2 = NULL;\n");
printf("int ***ptr_a3 = NULL;\n");
printf("Vamos atribuir um valor para a variavel (a): ");scanf("%d", &a);
printf("Variavel (a): %d\n", a);
printf("Endereco de memoria da variavel (a): &a = %d\n", &a);
tecle_anything();
printf("\rE agora, escrevendo: ptr_a1 = &a; ... como fica ?\n");
ptr_a1 = &a; // // ponteiro para a variável (a)
tecle_anything();
printf("\nPRIMEIRO NIVEL DE PONTEIRO:\n");
printf("\rConteudo do Ponteiro = endereco de memoria da variavel (a): ptr_a1 = %d\n", ptr_a1);
printf("Conteudo da variavel apontada = variavel (a): *ptr_a1 = %d\n", *ptr_a1);
printf("O Ponteiro tambem tem o seu proprio endereco: &ptr_a1 = %d\n\n", &ptr_a1);
tecle_anything();
printf("\rE agora, escrevendo: ptr_a2 = &ptr_a1; ... como fica ?\n");
ptr_a2 = &ptr_a1; // // ponteiro para ponteiro para a variável (a)
tecle_anything();
printf("\nSEGUNDO NIVEL DE PONTEIRO:\n");
printf("\rConteudo do Ponteiro = endereco de memoria do Ponteiro ptr_a1: ptr_a2 = %d\n", ptr_a2);
printf("Observe o conteudo que \x82 apontado pelo Ponteiro = *ptr_a2 = %d\n", *ptr_a2);
printf("Observe o conteudo que \x82 apontado pelo Ponteiro = **ptr_a2 = %d\n", **ptr_a2);
printf("O Ponteiro tambem tem o seu proprio endereco: &ptr_a2 = %d\n\n", &ptr_a2);
tecle_anything();
printf("\rE agora, escrevendo: ptr_a3 = &ptr_a2; ... como fica ?\n");
ptr_a3 = &ptr_a2; // // ponteiro para ponteiro para ponteiro para a variável (a)
tecle_anything();
printf("\nTERCEIRO NIVEL DE PONTEIRO:\n");
printf("\rConteudo do Ponteiro = endereco de memoria do Ponteiro ptr_a2: ptr_a3 = %d\n", ptr_a3);
printf("Observe o conteudo que \x82 apontado pelo Ponteiro = *ptr_a3 = %d\n", *ptr_a3);
printf("Observe o conteudo que \x82 apontado pelo Ponteiro = **ptr_a3 = %d\n", **ptr_a3);
printf("Observe o conteudo que \x82 apontado pelo Ponteiro = ***ptr_a3 = %d\n", ***ptr_a3);
printf("O Ponteiro tambem tem o seu proprio endereco: &ptr_a3 = %d\n\n", &ptr_a3);
system("pause");
}
}
// função tecle_anything
void tecle_anything()
{
printf("Digite qualquer tecla para continuar...");getch();
printf("\r \r");
}
Ótimo artigo!
E quem é Rimass no jogo do bixo???
Olá!
Muito bom esse curso. Uma dica remova o & antes das variáveis na função printf, pois este operador não é necessário ali. Abs!
Christopher, ele está dando printf no endereço, e não no valor. Quando se usa o & antes do nome da variável estamos falando do endereço dela. Deu pra ver que você não leu ou não entendeu.
ótima didática ! Parabéns pela abordagem e exemplos de um assunto tão importante que é ponteiros
Muito esse curso...explicações excelentes...muito didático...conciso, preciso, muito bom mesmo.
Quando rodei aqui, o código me retornou isso:
numero1:
Valor: 1
Endereco na memoria: 588443648
numero2:
Valor: 2
Endereco na memoria: 588443652
letra1:
Valor: a
Endereco na memoria: 588443646
letra2:
Valor: b
Endereco na memoria: 588443647
Alterando os valores...
o Endereço na memoria está me retornando "Lixo", certo?
Ótima explicação!
Postar um comentário