• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

hristosax

Não percebo o que são ponteiros

24 mensagens neste tópico

Estou a aprender C++, e estou na fase dos ponteiros.

mas não percebo o que são e o que fazem!

Alguem me explica?

Brigadinho.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Existe aqui no fórum um tópico em C com a mesma dúvida onde tens a explicação p isso.

Ponteiros em C são iguais aos de C++, podes ler a explicação á vontade.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

=D

o que nao percebo é Para quê fazer um ponteiro int chamado x, e apontá-lo para o "int num = 5", se simplesmente posso chamar o "num"!

Não percebo a utilidade.

Para quê ter um conjunto de ponteiros se posso ter uma array de variaveis?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Se quiseres alterar um valor que existe fora da função que de dar à função como argumento o ponteiro.

E um array é a mesma coisa que um conjunto de ponteiros.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Se quiseres alterar um valor que existe fora da função que de dar à função como argumento o ponteiro.

E um array é a mesma coisa que um conjunto de ponteiros.

Não percebo o teu português.

Sorry :/

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Queria dizer "tens de dar" :D

Vou dar um exemplo.

Se quiseres definir uma função que tem como argumento uma variável (e não o seu valor) e a coloca com o valor 0 usas o seguinte código:

void zero(int* n) {
  *n = 0;
}

Para fazer isto precisas do ponteiro que indique qual a variável que queres alterar, porque de outra forma a única coisa a que tinhas acesso dentro da função era o valor da variável (irrelevante neste contexto).

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Queria dizer "tens de dar" :D

Vou dar um exemplo.

Se quiseres definir uma função que tem como argumento uma variável (e não o seu valor) e a coloca com o valor 0 usas o seguinte código:

void zero(int* n) {
  *n = 0;
}

Para fazer isto precisas do ponteiro que indique qual a variável que queres alterar, porque de outra forma a única coisa a que tinhas acesso dentro da função era o valor da variável (irrelevante neste contexto).

acho que já estou a perceber...

hmmm..

Mesmo nao tendo aprendido funçoes, acho que estou a começar a perceber :)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

um ponteiro é simplesmente uma variável que em evz de guardar um valor ou o quer que seja, guarda um endereço de uma variável. pronto é só isto lol.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

ponteiro é em Brasileiro, em Português diz-se apontador  :P

Como o nome indica um apontador é algo que aponta para outra coisa neste caso (programação) um apontador é uma variavel em que o seu valor é um endereço de memória para outra

imagina que tens este pedaço de código

char x = 50;

char *pX = &x;

*px = 55;

agora supoe que a variavel x é colocada pelo gestor de memoria do teu os no endereço 0x00FF, o seu conteudo (valor) é posto a 50

agora imagina que o apontador px (k não é nada mais k uma variável)

é colocado no endereço 0x0100 e o seu valor no exemplo acima é initializado para ser o endereço da variavel x ou seja 0x00FF.

na 3º linha de código *px = 55;

o * atrás da variavel px é o operador de indirecção e basicamente o operador faz isto "vê qual o valor que px contem, que é 0x00FF acede á posição de memoria 0x00FF e altera o conteudo dessa mesma posiçao para o valor 55"

visuamente antes da 3º linha de código tens

|----------|

|    50    |  0x00FF

|----------|

| 0x00FF|  0x0100

|----------|

PS: espero que tenha sido clara a explicação o melhor mesmo se ainda te restarem duvidas é ires a  http://en.wikipedia.org/wiki/Pointer_(computing)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

ponteiro é em Brasileiro, em Português diz-se apontador  :P

Como o nome indica um apontador é algo que aponta para outra coisa neste caso (programação) um apontador é uma variavel em que o seu valor é um endereço de memória para outra

imagina que tens este pedaço de código

char x = 50;

char *pX = &x;

*px = 55;

agora supoe que a variavel x é colocada pelo gestor de memoria do teu os no endereço 0x00FF, o seu conteudo (valor) é posto a 50

agora imagina que o apontador px (k não é nada mais k uma variável)

é colocado no endereço 0x0100 e o seu valor no exemplo acima é initializado para ser o endereço da variavel x ou seja 0x00FF.

na 3º linha de código *px = 55;

o * atrás da variavel px é o operador de indirecção e basicamente o operador faz isto "vê qual o valor que px contem, que é 0x00FF acede á posição de memoria 0x00FF e altera o conteudo dessa mesma posiçao para o valor 55"

visuamente antes da 3º linha de código tens

|----------|

|    50    |  0x00FF

|----------|

| 0x00FF|  0x0100

|----------|

PS: espero que tenha sido clara a explicação o melhor mesmo se ainda te restarem duvidas é ires a  http://en.wikipedia.org/wiki/Pointer_(computing)

Eu percebo o que é, mas não percebo como USÁ-lo...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Ponteiros são muito mais usados em C, até porque não tens a vantagem da STL e portanto tens que implementar uma série de coisas.

À partida o teu programa só usa a memória que está definida em tempo de compilação. Por exemplo, se queres ter um array de inteiros, tens que definir qual o seu tamanho no código e não o podes alterar durante o programa.

int numeros[100];

Imagina agora que querias ter "uma espécie de array" mas em que pudesses alterar o seu tamanho e até inserir elementos no meio do array com facilidade. Para isso terias que usar dois conceitos de C: a estrutura e a alocação de memória dinâmica. Precisavas de uma estrutura com 2 elementos: um inteiro, e um apontador para a estrutura que representa o próximo elemento.

typedef struct list *lista;

struct list {
    int num;
    lista seguinte;
};

A isto se chama uma lista ligada. Repara que o tipo "lista" é um apontador para aquela estrutura. Para criar uma lista com um elemento que vale 30 fazes:

lista l = (lista)malloc(sizeof(struct list)); //Alocar memória durante a execução do programa
l -> num = 30;
l -> seguinte = NULL;

Em que o NULL significa que o apontador não aponta para nada (porque ainda não existe um elemento seguinte). É possível criar código para adicionar um elemento a uma lista no fim:

lista acrescenta(lista ll, int n) {
     lista iter = ll;
     while( iter -> seguinte != NULL) //enquanto existir um elemento a seguir
          iter = iter -> seguinte;
     iter -> seguinte = (lista) malloc(sizeof(struct list)); // alocar memória para o novo elemento
     iter -> seguinte -> num = n; //colocar o número no elemento que acabámos de criar
     iter -> seguinte -> seguinte = NULL; //assinalar o fim da lista
     return ll; //Repara que devolvemos o apontador que recebemos, porque o início da lista continua a ser o mesmo
}

E se quisermos, por exemplo, acrescentar um elemento no início da lista?

lista add_inicio(lista ll, int n) {
     novo = (lista) malloc(sizeof(struct list));
     novo -> num = n;
     novo -> seguinte = ll; //O elemento que vem a seguir ao que acrescentámos é primeiro da lista que recebemos
     return novo; //Devolvemos o elemento criado que é agora o primeiro elemento da nova lista
}

De forma igualmente simples podes, por exemplo, retirar um elemento do meio da lista, bastando para isso alterar o apontador "seguinte" do elemento que está antes dele para que passe a apontar para o elemento que vem a seguir. (E cuidado que é também necessário libertar a memória do elemento que retiramos)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

À partida o teu programa só usa a memória que está definida em tempo de compilação. Por exemplo, se queres ter um array de inteiros, tens que definir qual o seu tamanho no código e não o podes alterar durante o programa.

Em C já é possível criar arrays com um tamanho definido em run time: http://en.wikipedia.org/wiki/Variable-length_array

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Ponteiros são muito mais usados em C, até porque não tens a vantagem da STL e portanto tens que implementar uma série de coisas.

À partida o teu programa só usa a memória que está definida em tempo de compilação. Por exemplo, se queres ter um array de inteiros, tens que definir qual o seu tamanho no código e não o podes alterar durante o programa.

int numeros[100];

Imagina agora que querias ter "uma espécie de array" mas em que pudesses alterar o seu tamanho e até inserir elementos no meio do array com facilidade. Para isso terias que usar dois conceitos de C: a estrutura e a alocação de memória dinâmica. Precisavas de uma estrutura com 2 elementos: um inteiro, e um apontador para a estrutura que representa o próximo elemento.

typedef struct list *lista;

struct list {
    int num;
    lista seguinte;
};

A isto se chama uma lista ligada. Repara que o tipo "lista" é um apontador para aquela estrutura. Para criar uma lista com um elemento que vale 30 fazes:

lista l = (lista)malloc(sizeof(struct list)); //Alocar memória durante a execução do programa
l -> num = 30;
l -> seguinte = NULL;

Em que o NULL significa que o apontador não aponta para nada (porque ainda não existe um elemento seguinte). É possível criar código para adicionar um elemento a uma lista no fim:

lista acrescenta(lista ll, int n) {
     lista iter = ll;
     while( iter -> seguinte != NULL) //enquanto existir um elemento a seguir
          iter = iter -> seguinte;
     iter -> seguinte = (lista) malloc(sizeof(struct list)); // alocar memória para o novo elemento
     iter -> seguinte -> num = n; //colocar o número no elemento que acabámos de criar
     iter -> seguinte -> seguinte = NULL; //assinalar o fim da lista
     return ll; //Repara que devolvemos o apontador que recebemos, porque o início da lista continua a ser o mesmo
}

E se quisermos, por exemplo, acrescentar um elemento no início da lista?

lista add_inicio(lista ll, int n) {
     novo = (lista) malloc(sizeof(struct list));
     novo -> num = n;
     novo -> seguinte = ll; //O elemento que vem a seguir ao que acrescentámos é primeiro da lista que recebemos
     return novo; //Devolvemos o elemento criado que é agora o primeiro elemento da nova lista
}

De forma igualmente simples podes, por exemplo, retirar um elemento do meio da lista, bastando para isso alterar o apontador "seguinte" do elemento que está antes dele para que passe a apontar para o elemento que vem a seguir. (E cuidado que é também necessário libertar a memória do elemento que retiramos)

Acontece que não percebo quase nada do que puseste, ainda não sou tão avançado.

Sei o que são arrays, mas porque é que é que se tem de por um elemento com um POnteiro, e não simplesmente uma variável?!

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Vou tentar explicar sem código.

Porque em C toda a memória que tu utilizas está definida à partida. Se queres acrescentar coisas a meio, tens que fazer alocação dinâmica de memória. Isso corresponde a "pedir" para que o sistema te "arranje" mais memória para o que precisas.

Podes encarar a memória como sendo um array muito grande com milhões de posições. Quando pedes para arranjar um bloco de memória com um tamanho x, o sistema vai à procura de um local na memória que tenha esse espaço "vago", e devolve-te a posição onde começa esse bloco. Essa posição é um endereço de memória, que é o que fica guardado no ponteiro.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Vou tentar explicar sem código.

Porque em C toda a memória que tu utilizas está definida à partida. Se queres acrescentar coisas a meio, tens que fazer alocação dinâmica de memória. Isso corresponde a "pedir" para que o sistema te "arranje" mais memória para o que precisas.

Podes encarar a memória como sendo um array muito grande com milhões de posições. Quando pedes para arranjar um bloco de memória com um tamanho x, o sistema vai à procura de um local na memória que tenha esse espaço "vago", e devolve-te a posição onde começa esse bloco. Essa posição é um endereço de memória, que é o que fica guardado no ponteiro.

Então estás a falar do apontador como sendo o Endereço de memória e não a variável que lá vai?

Não percebo então porque precisas de um apontador para um lugar de uma array, se podes indicar esse lugar SOZINHO.

Por exemplo, para que é que precisas de dar o nome de "Golden Spot" ao lugar de estacionamento nº12, se podes simplesmente chamá-lo de "lugar de estacionamento nº 12"?

A mesma pergunta com as variáveis.

Precisas de chamar ao Sr. Mota "O empresário da rua 20" se podes chama-lo de Sr mota?

Eu sei que sou teimoso..

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Há uma diferença. As variáveis que tu declaras no teu programa também têm endereços (i.e. estão numa determinada posição da memória). Os apontadores referem-se a esses endereços de memória. Sempre que precisares de te referir a alguma coisa que possa estar em locais diferentes da memória tens que usar apontadores. Para experimentar podes fazer isto:

#include <stdio.h>
main()  {
   int a=10;
   int *apontador=&a; // &variavel, devolve o endereço de memória de uma variável
   printf("Valor de a: %d\n", a);
   printf("Endereço de a: %d\n", &a);
   printf("Valor do apontador: %d\n", apontador);
   printf("Endereço do apontador: %d\n", &apontador);
   printf("Valor na memória apontada pelo apontador: %d\n", *apontador);

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Há uma diferença. As variáveis que tu declaras no teu programa também têm endereços (i.e. estão numa determinada posição da memória). Os apontadores referem-se a esses endereços de memória. Sempre que precisares de te referir a alguma coisa que possa estar em locais diferentes da memória tens que usar apontadores. Para experimentar podes fazer isto:

#include <stdio.h>
main()  {
   int a=10;
   int *apontador=&a; // &variavel, devolve o endereço de memória de uma variável
   printf("Valor de a: %d\n", a);
   printf("Endereço de a: %d\n", &a);
   printf("Valor do apontador: %d\n", apontador);
   printf("Endereço do apontador: %d\n", &apontador);
   printf("Valor na memória apontada pelo apontador: %d\n", *apontador);

Ahh então estás a dizer que eu me posso usar o ponteiro para mudar ESSE endereço de memória, e que não posso fazer isso com a variável?

Então o ponteiro dá-me o poder de mudar o endereço de memória de int a, em vez de só ter acesso à variável?

Mas para que é que eu precisaria disso ? :P

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Por exemplo, se quiseres definir que duas variáveis se referem ao mesmo valor (e não a valores iguais).

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Ahh então estás a dizer que eu me posso usar o ponteiro para mudar ESSE endereço de memória, e que não posso fazer isso com a variável?

Então o ponteiro dá-me o poder de mudar o endereço de memória de int a, em vez de só ter acesso à variável?

Mas para que é que eu precisaria disso ? :P

O exemplo que dei atrás em que tens uma lista de elementos e queres inserir elementos no meio da lista é uma aplicação óbvia.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Vou também tentar dar uma explicação se me permitem :P

O que são apontadores já deves saber após tantas explicações, se bem perçebo a tua duvida está sim na sua utilidade.

Utilizações de apontadores:

1º Passagem de váriaveis por referencia ao invés de por valor, passo a explicar supondo que tens a seguinte função


inline Swap(int a, int b) {
int temp = a;
a = b;
b = a;
}

int main()
{
int x = 0, y = 1;
Swap(x, y);
}
[/Code]

ora como é obvio o pedaço de código acima não funciona como esperado, pois ao chamares Swap com as variáveis x e y, são criadas variáveis locais (a e b ) na função Swap e alterares essas cópias locais não afecta as variáveis x e y declaradas na função main a isto é o que se chama passagem por valor (value passing), com apontadores podes fazer o que se chama passagem por referencia.

[Code=cpp]
void Swap(int * pA, int *pB)
{
int temp = *pA;
*pA = *pB;
*pB = temp;
}
[/Code]

Trocando assim efectivamente o valor contido nas variáveis x e y declaradas na função main.

2º Uso de memoria dinamica, passo também a tentar explicar

Penso que á sabes que existem 2 importantes secções de memória a stack e heap(tambem chamada de free store entre outros nomes) a stack é onde são guardadas as variaveis declaradas no teu código, por exemplo

[Code=cpp]
int x;

char nome[255];
[/Code]

são exemplos de variáveis declaradas na stack

ora com apontadores passas a poder referenciar variaveis declaradas na heap o qual o seu tamanho não é necessário saberes á partida , por exemplo int *p = new int[valor_qualquer_pedido_ao_utilizador];

3º caso callbacks (eventos etc), imagina que estás a desenvolver uma biblioteca qualquer e queres fornecer ao teu cliente dessa biblioteca código que te crie um botão, por exemplo

[Code]
Button ok = MakeButton(..)
[/Code]

como é obvio esse butão ao ser clicado tu sabes k o cliente vai querer que despolete alguma acção, agora como tu és o fornecedor da biblioteca e não o cliente, o comportamento

de ao clicares no butão é definido pelo teu cliente e ão por ti. Então através de apontadores para funções podes, permitir que seja o cliente a dizer qual é

a função que deve ser chamada quando esse mesmo butão é clicado suponhamos por exemplo

[Code=cpp]
class Button
{
public:
void (*m_onclickHandler) (); // declaração de um apontador para uma função que retorna void e não reçebe argumentos.
.
.
.
};

void PrintMessage()
{
printf("olá mundo\n");
}

Button ok = MakeButton(argumentos, PrintMessage);
[/Code]

e prontos através do uso de apontadores para funções permites ao cliente da tua biblioteca definir qual função deve ser chamada quando o butão é clicado

claro k isto são só exemplos existem muitos mais usos para apontadores, de facto o exemplo acima do Swap é melhor resolvido com o uso de referencias, a melhor forma

de compreenderes, melhor o conceito e a utilidade de apontadores é mesmo atrávés da pratica, olhando para código de outros etc, e claro com uma boa referencia de leitura.

PS: desculpem lá o relatório

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Gostava de acrescentar outro exemplo à lista dada no post anterior que é a gestão de memória através de contagem de referências.

Por vezes, podes utilizar objectos que são necessários em vários pontos de um programa complexo. Objectos estes que podem vir a necessitar de muitos recursos, tais como memória. Com contagem por referência, podes ir criando vários apontadores ao mesmo objecto, fazer uma contagem cumulativa, e destruir o objecto assim que o número de apontadores necessários for reduzido a zero.

Os apontadores são muito úteis, principalmente para gerir recursos.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Vou também tentar dar uma explicação se me permitem :P

O que são apontadores já deves saber após tantas explicações, se bem perçebo a tua duvida está sim na sua utilidade.

Utilizações de apontadores:

1º Passagem de váriaveis por referencia ao invés de por valor, passo a explicar supondo que tens a seguinte função


inline Swap(int a, int b) {
int temp = a;
a = b;
b = a;
}

int main()
{
int x = 0, y = 1;
Swap(x, y);
}
[/Code]

ora como é obvio o pedaço de código acima não funciona como esperado, pois ao chamares Swap com as variáveis x e y, são criadas variáveis locais (a e b ) na função Swap e alterares essas cópias locais não afecta as variáveis x e y declaradas na função main a isto é o que se chama passagem por valor (value passing), com apontadores podes fazer o que se chama passagem por referencia.

[Code=cpp]
void Swap(int * pA, int *pB)
{
int temp = *pA;
*pA = *pB;
*pB = temp;
}
[/Code]

Trocando assim efectivamente o valor contido nas variáveis x e y declaradas na função main.

2º Uso de memoria dinamica, passo também a tentar explicar

Penso que á sabes que existem 2 importantes secções de memória a stack e heap(tambem chamada de free store entre outros nomes) a stack é onde são guardadas as variaveis declaradas no teu código, por exemplo

[Code=cpp]
int x;

char nome[255];
[/Code]

são exemplos de variáveis declaradas na stack

ora com apontadores passas a poder referenciar variaveis declaradas na heap o qual o seu tamanho não é necessário saberes á partida , por exemplo int *p = new int[valor_qualquer_pedido_ao_utilizador];

3º caso callbacks (eventos etc), imagina que estás a desenvolver uma biblioteca qualquer e queres fornecer ao teu cliente dessa biblioteca código que te crie um botão, por exemplo

[Code=cpp]
Button ok = MakeButton(..)
[/Code]

como é obvio esse butão ao ser clicado tu sabes k o cliente vai querer que despolete alguma acção, agora como tu és o fornecedor da biblioteca e não o cliente, o comportamento

de ao clicares no butão é definido pelo teu cliente e ão por ti. Então através de apontadores para funções podes, permitir que seja o cliente a dizer qual é

a função que deve ser chamada quando esse mesmo butão é clicado suponhamos por exemplo

[Code=cpp]
class Button
{
public:
void (*m_onclickHandler) (); // declaração de um apontador para uma função que retorna void e não reçebe argumentos.
.
.
.
};

void PrintMessage()
{
printf("olá mundo\n");
}

Button ok = MakeButton(argumentos, PrintMessage);
[/Code]

e prontos através do uso de apontadores para funções permites ao cliente da tua biblioteca definir qual função deve ser chamada quando o butão é clicado

claro k isto são só exemplos existem muitos mais usos para apontadores, de facto o exemplo acima do Swap é melhor resolvido com o uso de referencias, a melhor forma

de compreenderes, melhor o conceito e a utilidade de apontadores é mesmo atrávés da pratica, olhando para código de outros etc, e claro com uma boa referencia de leitura.

PS: desculpem lá o relatório

Agradeço a todos por dedicarem o vosso tempo à minha pergunta. Fiquei a perceber muito melhor, e vocês continuaram a ajudar mesmo eu sendo tão teimoso.

Ainda vou perceber melhor os apontadores quando começar a usá-los :D

Por agora chega de perguntas e respostas, CLOSE THE THREAD.

Muito obrigado,

Love P@P

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora