Ponteiros C (básico) [RESOLVIDO]

1. Ponteiros C (básico) [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 19/07/2015 - 14:35h

Boa tarde à todos;

Bom, eu passei esses dias estudando sobre ponteiros, assuntos básicos sobre eles (definição, declaração, inicialização, operações aritméticas / e incremento/decremento, comparações) e consegui entender sem grandes problemas.

Para testar estas explicações na prática, passei a fazer alguns testes (comentados) e, para saber se eles estão corretos, juntei todos eles num único programa e abaixo posto o código para que, por gentileza, alguém que já tenha esse conhecimento possa dar uma analisada e dizer se ele está correto ou não (a partir dos textos/comentários presentes no código).


Aqui está:

#include <stdio.h>
int main()
{
int num1=10, num2=30, *p, *pc;
p=&num1;
pc=&num1;

printf ("\n\nValor: %d", *p);
printf ("\nEndereço: %p", p);

/* A OPERAÇÃO ABAIXO FARÁ O PONTEIRO ANDAR
* 15 POSIÇÕES NA MEMÓRIA; COMO ESTAMOS
* TRABALHANDO COM INTEIROS, E INTEIROS,
* NA ARQUITETURA DE 32BITS PESAM 4 BYTES,
* O PONTEIRO SE DESLOCARÁ 3C NA MEMÓRIA */
p+=15;
printf ("\n\nValor: %d", *p);
printf ("\nEndereço: %p", p);

printf("\n--------");
printf("\nInicialmente, o ponteiro apontava ");
printf("para o endereço %p", pc);
printf("\nComo andamos 15 posições, onde cada ");
printf("posição do inteiro vale 4bytes, ");
printf("\nandamos 15 x 4 = 60bytes que, ");
printf("em hexadecimal, equivale a 3C bytes.");
printf("\nLogo, %p + 3C = %p\n", pc, (pc+15));

/* AGORA, O PONTEIRO p, DEPOIS DE TER
* DESLOCADO 15 INTEIROS, PASSARÁ A TER
* O MESMO CONTEÚDO DA VARIÁVEL NUM2, OU
* SEJA, PASSARÁ A VALER 20; O MESMO
* PONTEIRO, DE 15 POSIÇÕES ANTERIORES,
* CONTINUARÁ APONTANDO PARA A VARIÁVEL
* NUM1 E CONTINUARÁ VALENDO 10 */
*p=num2;
printf("\n\n*(p-15) = %d, (p-15) = %p", *(p-15), (p-15));
printf("\n*p = %d, p = %p", *p, p);

printf("\n--------");
printf("\nAgora, depois de atribuir o valor \"30\"");
printf("\npara o ponteiro (p+15), exibimos os valores");
printf("\ne os endereços dos ponteiros p (declarado");
printf("\nno início do programa) e (p+15) (que é o");
printf("\nponteiro p 15 posições adiante na memória");

/* (p+15) SERÁ p NOVAMENTE */
p-=15;
/* E SERÁ INCREMENTADO */
p++;
printf("\n\n*p = %d, p = %p", *(p-1), (p-1));
printf("\n*(p++) = %d, p++ = %p", *p, p);

printf("\n--------");
printf("\nAgora o ponteiro está apenas um inteiro");
printf("\nadiantado na memória em relação ao ponteiro");
printf("\n\"original\" (p)");

/* AGORA, O PONTEIRO p++ PASSARÁ A TER O
* MESMO VALOR DE NUM2 (30) */
*p=num2;
printf("\n\n*p = %d, p = %p", *p, p);

printf("\n--------");
printf("\nAcima, foi atribuído a p++ o valor de num2");

/* p++ SOFRE DECREMENTO E SERÁ IGUAL AO p
* ORIGINAL */
p--;


/***********************************************/
/* ABAIXO, OS RESULTADOS OBTIDOS SÃO IMPRESSOS */
/***********************************************/

printf("\n\n\tRESULTADOS:\n");
printf("\n\tNum1: %d", num1);
printf("\n\tNum2: %d", num2);

printf("\n\n\tPonteiro p: *p = %d, p = %p", *p, p);
printf("\n\tPonteiro p++: *(p++) = %d, p++ = %p", *(p+1), (p+1));
printf("\n\tPonteiro p+15: *(p+15) = %d, p+15 = %p", *(p+15), (p+15));
printf("\n\n");

return(0);
}


Ah, e só uma dúvida: qual é a diferença entre *(p++) e *p++?

Desde já, agradeço;


  


2. MELHOR RESPOSTA

Thiago Henrique Hüpner
Thihup

(usa Manjaro Linux)

Enviado em 19/07/2015 - 19:21h

unnslacker escreveu:

Muito obrigado pelas respostas!

Só gostaria de dizer algumas coisas:


#1 - Obrigado pela correção, mas no trecho abaixo eu acabei me expressando errado:

* O PONTEIRO SE DESLOCARÁ 3C NA MEMÓRIA */


Ponteiro não se desaloca sozinho!

Para desalocar um ponteiro, utilize o free.

free(ptr); 


Em "o ponteiro se deslocará..." não é o ponteiro que vai se deslocar, como meu comentário deu a entender, mas sim, o endereço que ele armazena será deslocado - neste caso, a afirmação estaria correta?

Se eu entendi ao certo, você disse que ele para de apontar para o endereço que ele aponta. Se for isso, está certo (pelo menos pelo que eu saiba). Mas no fim do programa tem de se lembrar de usar o free() para não causarmemory leak!


#2 - Fiquei com a seguinte dúvida sobre seu trecho, logo abaixo:

Num sistema de 32 bits pesam 4 bytes, sim, mas manipular memória desse jeito não é recomendável, por isso faça assim:

sizeof(int) 


Isso irá retornar o valor em bytes do determinado tipo, assim, ficando:

p+=15*sizeof(int); 



usando o sizeof(int) o retorno será 4, já que em sistemas de 32bits a variável inteira pesa 4bytes, certo?
então, a expressão p+=15*sizeof(int); equivaleria a p+=60, já que 15x4 = 60; minha dúvida seria: neste caso, o endereço apontado pelo ponteiro seria deslocado em 60 inteiros (60x4 = 240bytes) e não em 60bytes (15 inteiros), certo? (só para confirmar)


Bom, o compilador faz essa função, por exemplo:

p+=15 


Ele irá "interpretar como":

p+=15*4 


4 por que é o tamanho do int na sua arquitetura, mas para "ajudar" o compilador a não fazer besteira, e deixar seu código portavel, utilize o sizeof.

Ele separa a memória assim:


Posicao|Valor
------------------------
0|x
1|(aqui tambem serah do x)
2|(aqui tambem serah do x)
3|(aqui tambem serah do x)
4|(aqui esta livre)


Então quando fazemos ptr++ ele pula o numero de bytes do tipo, nesse caso o int, mas ele "traduziria" para algo mais ou menos assim:

ptr=ptr+sizeof(int) 

Ou seja:
ptr=ptr+4 


Ou seja, ele vai pular o numero de bytes reservados para o tipo de dado. (Esse compilador é incrível!)


#3 - Valeu, mais uma vez; então o *p++ simplesmente incrementa o valor armazenado na variável que o ponteiro aponta?

*(p++): Incrementa a posição da memória e "pega" o valor
*p++: Incrementa o valor da memória atual e "pega" o valor


Em teoria, sim.

Se eu estiver errado em qualquer coisa, me corrijam!

Espero ter ajudado

[]'s

T+

--
http://piadasnerds.com/wp-content/uploads/2011/08/grafico_souProgramador.png

3. Re: Ponteiros C (básico) [RESOLVIDO]

Thiago Henrique Hüpner
Thihup

(usa Manjaro Linux)

Enviado em 19/07/2015 - 15:02h

Bom, a maior parte do programa está correta, mas vou apontar (entendeu o trocadilho??) alguns erros no seu código:


/* A OPERAÇÃO ABAIXO FARÁ O PONTEIRO ANDAR
* 15 POSIÇÕES NA MEMÓRIA; COMO ESTAMOS
* TRABALHANDO COM INTEIROS, E INTEIROS,
* NA ARQUITETURA DE 32BITS PESAM 4 BYTES,
* O PONTEIRO SE DESLOCARÁ 3C NA MEMÓRIA */


Bom, aqui vejo 2 erros:

Num sistema de 32 bits pesam 4 bytes, sim, mas manipular memória desse jeito não é recomendável, por isso faça assim:

sizeof(int) 


Isso irá retornar o valor em bytes do determinado tipo, assim, ficando:

p+=15*sizeof(int); 


* O PONTEIRO SE DESLOCARÁ 3C NA MEMÓRIA */


Ponteiro não se desaloca sozinho!

Para desalocar um ponteiro, utilize o free.

free(ptr); 


Se eu estiver errado, me corrijam, mas se não me falha a memória:

*(p++): Incrementa a posição da memória e "pega" o valor
*p++: Incrementa o valor da memória atual e "pega" o valor

Espero ter ajudado

[]'s

T+

--
http://piadasnerds.com/wp-content/uploads/2011/08/grafico_souProgramador.png


4. Re: Ponteiros C (básico) [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 19/07/2015 - 15:29h

Muito obrigado pelas respostas!

Só gostaria de dizer algumas coisas:


#1 - Obrigado pela correção, mas no trecho abaixo eu acabei me expressando errado:

* O PONTEIRO SE DESLOCARÁ 3C NA MEMÓRIA */


Ponteiro não se desaloca sozinho!

Para desalocar um ponteiro, utilize o free.

free(ptr); 


Em "o ponteiro se deslocará..." não é o ponteiro que vai se deslocar, como meu comentário deu a entender, mas sim, o endereço que ele armazena será deslocado - neste caso, a afirmação estaria correta?


#2 - Fiquei com a seguinte dúvida sobre seu trecho, logo abaixo:

Num sistema de 32 bits pesam 4 bytes, sim, mas manipular memória desse jeito não é recomendável, por isso faça assim:

sizeof(int) 


Isso irá retornar o valor em bytes do determinado tipo, assim, ficando:

p+=15*sizeof(int); 



usando o sizeof(int) o retorno será 4, já que em sistemas de 32bits a variável inteira pesa 4bytes, certo?
então, a expressão p+=15*sizeof(int); equivaleria a p+=60, já que 15x4 = 60; minha dúvida seria: neste caso, o endereço apontado pelo ponteiro seria deslocado em 60 inteiros (60x4 = 240bytes) e não em 60bytes (15 inteiros), certo? (só para confirmar)


#3 - Valeu, mais uma vez; então o *p++ simplesmente incrementa o valor armazenado na variável que o ponteiro aponta?

*(p++): Incrementa a posição da memória e "pega" o valor
*p++: Incrementa o valor da memória atual e "pega" o valor



5. Re: Ponteiros C (básico) [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 19/07/2015 - 19:43h

Muito obrigado pela ajuda e pelas explicações, elas tiraram algumas dúvidas e afirmaram o que eu "suspeitava" estar correto;


6. Re: Ponteiros C (básico) [RESOLVIDO]

Thiago Henrique Hüpner
Thihup

(usa Manjaro Linux)

Enviado em 19/07/2015 - 19:44h

Disponha!

Se quiser, me adicione no Skype: thihup

Espero ter ajudado

[]'s

T+

--
http://piadasnerds.com/wp-content/uploads/2011/08/grafico_souProgramador.png


7. Re: Ponteiros C (básico) [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 20/07/2015 - 02:01h

Thihup escreveu:

Se eu estiver errado em qualquer coisa, me corrijam!


Thihup, você está errado, sim.

Na aritmética de ponteiros, você nunca deve usar sizeof. O compilador sabe o tamanho do dado apontado. Vejas as seguintes relações (uso sintaxe de C++, só para deixar claro que conversões entre ponteiros e inteiros normalmente devem ser evitadas).

int *pi1, *pi2;
int ai[2];
intptr_t abs_addr1, abs_addr2;

pi1=&ai[0];
pi2=pi+1;
assert(pi2==&ai[1]); // Não vai dar erro.

assert((pi2-pi1)==1); // Não vai dar erro.

abs_addr1=reinterpret_cast<intptr_t>(pi1);
abs_addr2=reinterpret_cast<intptr_t>(pi2);
assert((abs_addr2-abs_addr1)==sizeof(int)); // Não vai dar erro.



8. Re: Ponteiros C (básico) [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 20/07/2015 - 02:10h

Thihup escreveu:

* O PONTEIRO SE DESLOCARÁ 3C NA MEMÓRIA */


Ponteiro não se desaloca sozinho!

Para desalocar um ponteiro, utilize o free.


Ele escreveu “deslocar”, não “desalocar”.

Se eu estiver errado, me corrijam, mas se não me falha a memória:

*(p++): Incrementa a posição da memória e "pega" o valor
*p++: Incrementa o valor da memória atual e "pega" o valor


Está errado. Os dois são absolutamente sinônimos: o valor original de p é usado para obter o dado apontado, e depois disso o valor de p (não o dado que ele aponta!) é incrementado.


9. Re: Ponteiros C (básico) [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 20/07/2015 - 02:45h

unnslacker escreveu:


int main()
{
int num1=10, num2=30, *p, *pc;
p=&num1;
pc=&num1;

/* [... Bloco suprimido ...] */

p+=15;

/* [... Bloco suprimido ...] */

/* AGORA, O PONTEIRO p, DEPOIS DE TER
* DESLOCADO 15 INTEIROS, PASSARÁ A TER
* O MESMO CONTEÚDO DA VARIÁVEL NUM2, OU
* SEJA, PASSARÁ A VALER 20; O MESMO
* PONTEIRO, DE 15 POSIÇÕES ANTERIORES,
* CONTINUARÁ APONTANDO PARA A VARIÁVEL
* NUM1 E CONTINUARÁ VALENDO 10 */
*p=num2;


Na verdade, aí você tem um problema. O valor de p não aponta para uma região de memória que tenha sido preparada para conter um inteiro. Na verdade, você sabe que o endereço dele é o de uma posição que está 15*sizeof(int) bytes depois da posição ocupada por num1, mas provavelmente não tem a menor ideia de o que existe nessa posição. Não sabe nem mesmo se essa posição foi entregue pelo sistema operacional para o seu programa. Ler ou escrever nessa posição é uma temeridade. É realmente um erro.


/* [... Bloco suprimido ...] */

/* (p+15) SERÁ p NOVAMENTE */
p-=15;
/* E SERÁ INCREMENTADO */
p++;

/* [... Bloco suprimido ...] */

/* AGORA, O PONTEIRO p++ PASSARÁ A TER O
* MESMO VALOR DE NUM2 (30) */
*p=num2;


Duas observações. A primeira é que você comete aqui o mesmo erro já apontado acima: mexe num lugar que não sabe se pode mexer. Enquanto p apontava para num você sabia que ele apontava para um lugar válido da memória, mas quando você o incrementou, fez com que ele apontasse uma uma posição além de todos os bytes ocupados por num1. Pode ser que nessa posição resida num2 ou outra veriável, mas pode também ser um dado de controle do programa, ou pode ser que o endereço em questão nem tenha sido entregue ao programa pelo sistema operacional.

A segunda é que você jác inrementara p mais acima. Quando repetiu “p++” dentro do comentário que está algumas linhas abaixo, deu margem a entender que gostaria de incrementá-lo uma segunda vez. Pense e aja do seguinte modo: depois que você o incrementou, p aponta para um local novo. Ponto.


10. Re: Ponteiros C (básico) [RESOLVIDO]

Thiago Henrique Hüpner
Thihup

(usa Manjaro Linux)

Enviado em 20/07/2015 - 08:07h

Obrigado pelas correções, Paulo!

Fazia um tempo que não mexia com ponteiros e memória "diretamente" (só com o uso de estruturas e classes, etc).

Agora não cometo mais esses erros!

Paulo, você poderia ler um e-mail que mandei pra você? Gostaria de sua opinião a respeito de um novo artigo.

[]'s

T+

--
http://piadasnerds.com/wp-content/uploads/2011/08/grafico_souProgramador.png


11. Re: Ponteiros C (básico) [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 20/07/2015 - 12:25h

paulo1205 escreveu:

unnslacker escreveu:


int main()
{
int num1=10, num2=30, *p, *pc;
p=&num1;
pc=&num1;

/* [... Bloco suprimido ...] */

p+=15;

/* [... Bloco suprimido ...] */

/* AGORA, O PONTEIRO p, DEPOIS DE TER
* DESLOCADO 15 INTEIROS, PASSARÁ A TER
* O MESMO CONTEÚDO DA VARIÁVEL NUM2, OU
* SEJA, PASSARÁ A VALER 20; O MESMO
* PONTEIRO, DE 15 POSIÇÕES ANTERIORES,
* CONTINUARÁ APONTANDO PARA A VARIÁVEL
* NUM1 E CONTINUARÁ VALENDO 10 */
*p=num2;


Na verdade, aí você tem um problema. O valor de p não aponta para uma região de memória que tenha sido preparada para conter um inteiro. Na verdade, você sabe que o endereço dele é o de uma posição que está 15*sizeof(int) bytes depois da posição ocupada por num1, mas provavelmente não tem a menor ideia de o que existe nessa posição. Não sabe nem mesmo se essa posição foi entregue pelo sistema operacional para o seu programa. Ler ou escrever nessa posição é uma temeridade. É realmente um erro.


/* [... Bloco suprimido ...] */

/* (p+15) SERÁ p NOVAMENTE */
p-=15;
/* E SERÁ INCREMENTADO */
p++;

/* [... Bloco suprimido ...] */

/* AGORA, O PONTEIRO p++ PASSARÁ A TER O
* MESMO VALOR DE NUM2 (30) */
*p=num2;


Duas observações. A primeira é que você comete aqui o mesmo erro já apontado acima: mexe num lugar que não sabe se pode mexer. Enquanto p apontava para num você sabia que ele apontava para um lugar válido da memória, mas quando você o incrementou, fez com que ele apontasse uma uma posição além de todos os bytes ocupados por num1. Pode ser que nessa posição resida num2 ou outra veriável, mas pode também ser um dado de controle do programa, ou pode ser que o endereço em questão nem tenha sido entregue ao programa pelo sistema operacional.

A segunda é que você jác inrementara p mais acima. Quando repetiu “p++” dentro do comentário que está algumas linhas abaixo, deu margem a entender que gostaria de incrementá-lo uma segunda vez. Pense e aja do seguinte modo: depois que você o incrementou, p aponta para um local novo. Ponto.


Sim, eu me arrisquei fazendo isso, rsrsrssrsr
Quis tentar ver na prática sobre as operações e os incrementos com os ponteiros, e essa foi a única forma que encontrei para fazê-lo;

Então, só mais outra pergunta: quando, exatamente, eu poderia fazer essas operações (em uma situação real)? eu só posso envolver ponteiros "conhecidos" nessas operações?

Obrigado!


12. Re: Ponteiros C (básico) [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 20/07/2015 - 16:20h

Você pode deslocar ponteiros ao longo de arrays, tanto os explicitamente declarados como tal no código fonte (e.g. int arr_int[100];) quantos os dinamicamente alocados (e.g. int *dyn_arr_int=malloc(100*sizeof dyn_arr_int[0]); em C, ou int *dyn_arr_int=new int[100]; em C++).

Os padrões do C e do C++ garante aritmética de ponteiros válida quando você usa um ponteiro para percorrer o mesmo array, desde o endereço do primeiro alemento até o endereço que seria o correspondente a um elemento após o fim.

int a1_i[50], a2_i[50];
int *p1_i, *p2_i;
ptrdiff_t dist1, dist2;
intptr_t abs_addr1, abs_addr2;

p1_i=a1_i; // Aponta para início do array a1_i.
p2_i=&a2_i[50]; // Válido: aponta posição imediatamente após fim do array.
/*
Note que a construção “&a2_i[50]” não faz acesso ao 51º elemento (o que
seria inválido) e depois pega seu endereço: o compilador é inteligente o
suficiente para, ao ver o operador “&”, apenas calcular o endereço do que
_seria_ o 51º elemento, e usar esse endereço calculado diretamente na
expressão.
*/

dist1=p1_i-a1_i; // OK. dist1 vale zero, pois, p1_i==a1_i;
dist2=p2_i-a2_i; // OK: dist2 vale 50, que é o número de elementos
// entre o início do array e uma posição após o fim
// (ou seja: o nº de elemntos do array).

a1_i[dist1]=dist1;
a1_i[dist2-1]=dist2-1;

/*
As operações abaixo não vão dar erro de compilação, mas o padrão
deixa indefinido o comportamento de apontar para antes do início do
array ou além de uma posição após o fim. Provavelmente dist1 e dist2
valerão, respectivamente, -1 e 51, mas o padrão NÃO GARANTE isso.
*/
p1_i--;
p2_i++;
dist1=p1_i-a1_i;
dist2=p2_i-a2_i;

/* Volta a valores sadios. */
p1_i=a1_i; // Aponta para início do array a1_i.
p2_i=&a2_i[50]; // Válido: aponta posição imediatamente após fim do array.

/*
Mais operações que possivelmente não vão dar erro de compilação, mas
são abusivas em relação ao padrão, que só garante a validade de resultados
de aritmética de ponteiros que operem nos limites [0;N] de um mesmo array
(onde N é o número de elementos desse array, e N pode ser usado para arit-
mética mas não como índice para acesso a elemento). Possivelmente os va-
lores salvos em dist1 e dist2 serão valores finitos, mas que certamente não
poderão ser usados como índices em nenhum dos dois arrays a1_i e a2_i.
*/
dist1=p2_i-p1_i;
dist2=a2_i-a1_i;

/*
Converte os endereços diretamente para inteiros -- algo que só deve ser
feito se estritamente necessário.
*/
abs_addr1=(intptr_t)a1_i; // Converte início de a1_i para inteiro comum.
abs_addr2=(intptr_t)a2_i; // Converte início de a2_i para inteiro comum.

/*
O tipo intptr_i é um inteiro comum, então a aritmética sobre desse tipo é
totalmente ordinária: o compilador não tem como saber tamanho de ele-
mentos nem nada parecido.
*/
abs_addr1+=sizeof(int); // Para ir para próximo elemento, tenho de ser explícito.
abs_addr2+=1;

p1_i=(int *)abs_addr1;
assert(p1_i==&a1_i[1]); // Não vai dar erro.

p2_i=(int *)abs_addr2; // Problema!
*p2_i=1234; // Problema sério: pode ser que o programa capote, pode ser que
// fique mais lento. Se funcionar, vai modificar um pedaço de a2_i[0]
// um pedaço de a2_i[1];







Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts