Quais problemas de programação são melhor resolvidos usando ponteiros? [fechadas]

58

Bem, eu basicamente entendo como usar ponteiros, mas não como melhor usá-los para melhorar a programação.

Quais são os bons projetos ou problemas a serem resolvidos envolvendo o uso de ponteiros para que eu possa entendê-los melhor?

disoco
fonte
Pergunte sobre quais problemas de programação são melhor resolvidos usando ponteiros, não quais projetos / programas. Você pode escrever um programa usando ponteiros ou não - eles (projetos) são simplesmente uma construção muito grande para uma resposta significativa.
Oded
37
Problemas de programação são criados usando ponteiros, não resolvidos. :)
xpda
4
Na verdade, eu me pergunto quais problemas podem ser resolvidos sem ponteiro. Muito poucos, com certeza.
Deadalnix 11/09/11
4
@deadalnix, depende se você quer dizer ponteiros explícitos ou ponteiros implícitos. Se você quer dizer ponteiros implícitos (ou seja, existem ponteiros usados ​​em algum lugar), seria impossível escrever qualquer programa que use a pilha ou a pilha. Se você quer dizer ponteiros explícitos, não tem restrições, pode fazer qualquer tamanho de alocação criando novos quadros de pilha (basicamente usando uma chamada de função) e desalocação usando chamadas finais, assumindo que elas sejam otimizadas pelo compilador.
dan_waterworth
3
ugh, algumas dessas respostas são terríveis.

Respostas:

68

Manipular grandes quantidades de dados na memória é onde os ponteiros realmente brilham.

Passar um objeto grande por referência é equivalente a apenas passar um número antigo simples. Você pode manipular as peças necessárias diretamente, em vez de copiar um objeto, alterá-lo e, em seguida, devolver a cópia a ser colocada no lugar do original.

user7007
fonte
12
Essa é uma ótima resposta e eu acrescentaria que você não precisa de objetos grandes para manipular grandes quantidades de dados; Ocasionalmente, manipular objetos grandes fará isso, mas um problema ainda mais comum é manipular um monte de objetos pequenos com muita frequência. Que, se você pensar bem, é praticamente todos os programas de computador.
Jhocking
44

O conceito de ponteiro permite que você consulte os dados por endereço sem duplicar o armazenamento de dados. Essa abordagem permite escrever algoritmos eficientes, como:

  1. Classificação
    Ao mover dados em um algoritmo de classificação, você pode mover o ponteiro em vez dos dados em si - pense em classificar milhões de linhas em uma sequência de 100 caracteres; você salva muitos movimentos de dados desnecessários.

  2. Listas vinculadas
    Você pode armazenar o próximo e / ou local do item anterior e não todos os dados associados ao registro.

  3. Passando parâmetros
    Nesse caso, você passa o endereço dos dados em vez dos próprios dados. Mais uma vez, pense em um algoritmo de compactação de nome executado em milhões de linhas.

O conceito pode ser estendido para estruturas de dados, como bancos de dados relacionais, em que um ponteiro é semelhante a uma chave estrangeira . Alguns idiomas não incentivam o uso de ponteiros como C # e COBOL.

Exemplos podem ser encontrados em muitos lugares, como:

A postagem a seguir pode ser relevante de alguma forma:

NoChance
fonte
14
Eu não diria que o C # não incentiva o uso de ponteiros; em vez disso, não incentiva o uso de ponteiros não gerenciados - há muitas coisas em C # que são passadas por referência em vez de valor.
Blakomen 12/09
Boa, boa explicação
Sabby
5
Teve que votar para o comentário em C #. As "referências" em C # são equivalentes aos ponteiros, mas o C # abstrai você de saber que realmente está lidando com um ponteiro para um objeto no heap gerenciado.
MattDavey
11
@ MattDavey: referências em C # não são ponteiros. Você não pode adicionar a partir deles, usá-los para indexar uma matriz, usar múltiplos direcionamentos, etc. Você não pode ter referências a referências, por exemplo.
Billy ONeal
5
@ Billy ONeal, a noção de que "é apenas um ponteiro se você puder fazer aritmética" é francamente ridícula, mas não tenho paciência para discutir com você.
MattDavey
29

Estou surpreso que nenhuma outra resposta tenha mencionado isso: os ponteiros permitem criar estruturas de dados não-contíguas e não-lineares, em que um elemento pode estar relacionado a vários outros de maneiras complexas.

Listas vinculadas (singulares, duplas e circulares), árvores (vermelho-preto, AVL, trie, binário, particionamento de espaço ...) e gráficos são exemplos de estruturas que podem ser construídas mais naturalmente em termos de referências do que em valores .

Jon Purdy
fonte
Você está esquecendo a lista vinculada do XOR [=
dan_waterworth 12/09
@dan_waterworth: Não. Conheço muito bem a magia negra.
11119 Jon Purdy
8

Uma maneira simples é no polimorfismo. Polimorfismo funciona apenas com ponteiros.

Além disso, você usa ponteiros sempre que precisar de alocação dinâmica de memória. Em C, isso geralmente acontece quando você precisa armazenar dados em uma matriz, mas não sabe o tamanho em tempo de compilação. Você chamaria o malloc para alocar a memória e um ponteiro para acessá-la. Além disso, se você sabe ou não, quando você usa uma matriz, você está usando ponteiros.

for(int i = 0; i < size; i++)
   std::cout << array[i];

é o equivalente a

for(int i = 0; i < size; i++)
   std::cout << *(array + i);

Esse conhecimento permite que você faça coisas realmente legais, como copiar uma matriz inteira em uma linha:

while( (*array1++ = *array2++) != '\0')

No c ++, você usa new para alocar memória para um objeto e armazená-lo em um ponteiro. Você faz isso sempre que precisar criar um objeto durante o tempo de execução, em vez de durante o tempo de compilação (ou seja, um método cria um novo objeto e o armazena em uma lista).

Para entender melhor os ponteiros:

  1. Encontre alguns projetos que brincam com c-strings e matrizes tradicionais.
  2. Encontre alguns projetos que usam herança.

  3. Aqui está o projeto em que eu cortei meus dentes:

Leia duas matrizes nxn de um arquivo e execute as operações básicas de espaço vetorial nelas e imprima o resultado na tela.

Para fazer isso, é necessário usar matrizes dinâmicas e consultar suas matrizes por ponteiros, pois você terá duas matrizes de matrizes (matrizes dinâmicas multidimensionais). Depois de concluir esse projeto, você terá uma boa idéia de como usar ponteiros.

Jonathan Henson
fonte
2
"O polimorfismo funciona apenas com ponteiros." Não preciso: o polimorfismo também funciona com referências. Quais são, em última análise, indicadores, mas acho que a distinção é importante, especialmente para os novatos.
Laurent Pireyn 11/09/11
seu exemplo para copiar uma matriz em uma linha é realmente para copiar uma sequência terminada em zero em uma linha, não em nenhuma matriz geral.
tcrosley
8

Para entender realmente por que os ponteiros são importantes, você precisa entender a diferença entre alocação de heap e alocação de pilha.

A seguir, é apresentado um exemplo de alocação de pilha:

struct Foo {
  int bar, baz
};

void foo(void) {
  struct Foo f;
}

Os objetos alocados na pilha existem apenas durante a execução da função atual. Quando a chamada para foosai do escopo, a variável também f.

Um caso em que isso se torna um problema é quando você precisa retornar algo diferente de um tipo integral de uma função (por exemplo, a estrutura Foo do exemplo acima).

Por exemplo, a função a seguir resultaria no chamado "comportamento indefinido".

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  struct Foo f;
  return &f;
}

Se você deseja retornar algo como struct Foo *de uma função, o que você realmente precisa é de uma alocação de heap:

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  return malloc(sizeof(struct Foo));
}

A função mallocaloca um objeto na pilha e retorna um ponteiro para esse objeto. Observe que o termo "objeto" é usado livremente aqui, significando "algo" em vez de objeto no sentido de programação orientada a objetos.

A vida útil dos objetos alocados por heap é controlada pelo programador. A memória para este objeto será reservada até que o programador o libere, ou seja, chamando free()ou até o programa sair.

Editar : não percebi que esta pergunta está marcada como uma pergunta em C ++. Os operadores C ++ newe new[]executam a mesma função que malloc. Os operadores deletee delete[]são análogos a free. Embora newe deletedeva ser usado exclusivamente para alocar e liberar objetos C ++, o uso malloce freeé perfeitamente legal no código C ++.

Mike Steinert
fonte
11
(+1), também conhecidos como "variáveis estáticas atribuídas" e "variáveis alocados dinâmicos" ;-)
umlcat
4

Escreva qualquer projeto não trivial em C e você precisará descobrir como / quando usar ponteiros. No C ++, você utilizará principalmente objetos habilitados para RAII que gerenciam ponteiros internamente, mas no C os ponteiros brutos têm uma função muito mais predominante. Quanto ao tipo de projeto que você deve fazer, pode ser algo não trivial:

  • Um servidor web pequeno e simples
  • Ferramentas de linha de comando Unix (menos, gato, classificação etc.)
  • Algum projeto real que você deseja fazer por si só e não apenas para aprender

Eu recomendo o último.

Viktor Dahl
fonte
Então, eu apenas vou para um projeto que eu quero fazer (como um jogo 2D) e supõe-se que eu precisarei e aprenderei dicas?
Disoco
3
Na minha experiência, fazer projetos reais que estão um pouco fora do seu alcance em termos de habilidades é a melhor maneira de aprender. Os conceitos podem ser aprendidos lendo e escrevendo pequenos programas de "brinquedos". No entanto, para realmente aprender como você pode usar esses conceitos para escrever programas melhores, basta escrever programas que não sejam de brinquedo.
Viktor Dahl
3

Quase todos os problemas de programação que podem ser resolvidos com ponteiros podem ser resolvidos com outros tipos mais seguros de referências (não se referindo a referências em C ++ , mas o conceito geral de CS de ter uma variável refere-se ao valor dos dados armazenados em outro local).

Os ponteiros por serem uma implementação específica de baixo nível de referências, onde é possível manipular diretamente endereços de memória, são muito poderosos, mas podem ser levemente perigosos (por exemplo, apontar para locais de memória fora do programa).

O benefício de usar ponteiros diretamente é que eles serão um pouco mais rápidos, sem a necessidade de fazer verificações de segurança. Idiomas como Java, que não implementam diretamente ponteiros no estilo C, sofrerão um pequeno impacto no desempenho, mas reduzirão muitos tipos de situações difíceis de depurar.

Quanto ao motivo pelo qual você precisa de indireção, a lista é bastante longa, mas essencialmente as duas principais ideias são:

  1. A cópia de valores de objetos grandes é lenta e fará com que o objeto seja armazenado na RAM duas vezes (potencialmente muito caro), mas a cópia por referência é quase instantânea, usando apenas alguns bytes de RAM (para o endereço). Por exemplo, digamos que você tenha ~ 1000 objetos grandes (cada um com cerca de 1 MB de RAM) na memória e seu usuário precise selecionar o objeto atual (que será acionado pelo usuário). Ter uma variável selected_objectque é uma referência a um dos objetos é muito mais eficiente do que copiar o valor do objeto atual em uma nova variável.
  2. Ter estruturas de dados complicadas que se referem a outros objetos, como listas ou árvores vinculadas, em que cada item na estrutura de dados se refere a outros itens na estrutura de dados. O benefício de se referir a outros itens na estrutura de dados significa que você não precisa mover todos os itens da lista na memória apenas porque inseriu um novo item no meio da lista (pode haver inserções de tempo constantes).
dr jimbob
fonte
2

A manipulação de imagem no nível de pixel é quase sempre mais fácil e rápida usando ponteiros. Às vezes, só é possível usando ponteiros.

Dave Nay
fonte
11
Eu não acho que a afirmação "às vezes só é possível usando ponteiros" seja verdadeira. Existem idiomas que não expõem ponteiros para o desenvolvedor, mas podem ser usados ​​para resolver problemas no mesmo espaço.
Thomas Owens
Talvez ... não conheço todos os idiomas disponíveis, mas certamente não gostaria de escrever uma rotina para aplicar um filtro de convolução a uma imagem usando uma linguagem que não possui ponteiros.
Dave Nay
@ Thomas, enquanto você estiver correto, a pergunta é sobre c ++, que expõe os ponteiros para o desenvolvedor.
Jonathan Henson
@ Jonathan Mesmo assim, se você pode resolver um problema em um idioma que não expõe ponteiros, também é possível resolver esse problema em um idioma que expõe ponteiros sem usar ponteiros. Isso torna a primeira frase verdadeira, mas a segunda frase falsa - não há (pelo que sei) problemas que não possam ser resolvidos sem o uso de ponteiros.
Thomas Owens
11
Pelo contrário, o processamento de imagens geralmente envolve "aritmética de coordenadas" - digamos, tomando o seno e o cosseno de um ângulo e multiplicando-o pelas coordenadas xe y. Em outros casos, é necessária a replicação de pixel envolvente ou fora da borda. Nesse sentido, a aritmética do ponteiro é bastante inadequada.
rwong
1

Os ponteiros são usados ​​em muitas linguagens de programação abaixo da superfície sem incomodar o usuário. C / C ++ apenas fornece acesso a eles.

Quando usá-los: Sempre que possível, porque copiar dados é ineficiente. Quando não usá-los: Quando você deseja duas cópias que podem ser alteradas individualmente. (O que basicamente acabará copiando o conteúdo do objeto_1 para outro lugar na memória e retornando um ponteiro - desta vez apontando para o objeto_2)

mmlac
fonte
1

Ponteiros são uma parte essencial para qualquer implementação de estrutura de dados em C e estruturas de dados são uma parte essencial de qualquer programa não trivial.

Se você quiser saber por que os ponteiros são tão vitais, sugiro aprender o que é uma lista vinculada e tentar escrever uma sem usar ponteiros. Eu não defendi um desafio impossível para você (DICA: ponteiros são usados ​​para fazer referência a locais na memória, como você faz referência a coisas em matrizes?).

dan_waterworth
fonte
+1 por mencionar estruturas de dados, o uso em algoritmos é uma consequência natural.
Matthieu M.
0

Como um exemplo da vida real, construindo uma carteira de pedidos com limite.

o feed ITCH 4.1, por exemplo, tem um conceito de "substituir" pedidos, onde os preços (e, portanto, a prioridade) podem mudar. Você deseja receber pedidos e movê-los para outro lugar. A implementação de uma fila dupla com ponteiros facilita a operação.

Foo Bah
fonte
0

Examinando os vários sites StackExchange, percebo que está em voga fazer uma pergunta como esta. Correndo o risco de críticas e votos negativos, serei honesto. Isso não pretende trollar ou inflamar, só pretendo ajudar, dando uma avaliação honesta da questão.

E essa avaliação é a seguinte: Essa é uma pergunta muito estranha a ser feita a um programador em C. Praticamente tudo o que diz é "eu não conheço C." Se eu analisar um pouco mais e mais cinicamente, há um tom de seguimento para essa "pergunta": "Existe algum atalho que eu posso tomar para adquirir rápida e repentinamente o conhecimento de um programador C experiente, sem dedicar nenhum tempo? para estudo independente? " Qualquer "resposta" que alguém possa dar não substitui a tarefa de entender o conceito subjacente e seu uso.

Eu sinto que é mais construtivo aprender C bem, em primeira mão, do que ir à Web e perguntar isso às pessoas. Quando você conhece C bem, não se incomodará em fazer perguntas como essa, será como perguntar "que problemas são melhor resolvidos com uma escova de dentes?"

asveikau
fonte
0

Embora os ponteiros realmente brilhem enquanto trabalha com grandes objetos de memória, ainda há uma maneira de fazer o mesmo sem eles.

O ponteiro é absolutamente essencial para a chamada programação dinâmica , é quando você não sabe quanta memória será necessária antes da execução do programa. Na programação dinâmica, você pode solicitar trechos de memória durante o tempo de execução e colocar seus próprios dados neles - portanto, você precisa de ponteiro ou referências (a diferença não é importante aqui) para poder trabalhar com esses trechos de dados.

Desde que você possa reivindicar determinada memória durante o tempo de execução e colocar seus dados na memória recém-adquirida, poderá fazer o seguinte:

  1. Você pode ter estruturas de dados com extensão automática. Essas são estruturas que podem se estender reivindicando memória adicional, desde que sua capacidade se esgote. A propriedade chave de toda estrutura auto-extensível é que ela consiste em pequenos blocos de memória (denominados nós, itens de lista etc., dependendo da estrutura) e cada bloco contém referências a outros blocos. Essas estruturas "vinculadas" constroem a maioria dos tipos de dados modernos: gráficos, árvores, listas etc.

  2. Você pode programar usando o paradigma OOP (Programação Orientada a Objetos). Todo o POO baseia-se no uso de variáveis ​​não diretas, mas em referências a instâncias de classe (denominadas objetos) e na manipulação delas. Nenhuma instância única pode existir sem ponteiros (embora seja possível usar classes somente estáticas, mesmo sem ponteiros, isso é uma exceção).

Alexander Galkin
fonte
0

Engraçado, acabei de responder uma pergunta sobre C ++ e falei sobre ponteiros.

Versão curta: NUNCA precisa de ponteiros, a menos que 1) a biblioteca que você está usando o force 2) Você precisa de uma referência nula.

Se você precisar de um array, lista, string, etc, basta colocá-lo na pilha e usar um objeto stl. O retorno ou a passagem de objetos stl são rápidos (fato não verificado) porque eles têm código interno que copia um ponteiro em vez de um objeto e só copia os dados se você gravar nele. Este é C ++ regular, nem mesmo o novo C ++ 11, que tornará mais fácil para os criadores de bibliotecas.

Sua pergunta pode ser respondida nesta parte

Se você usar um ponteiro, verifique se está em uma dessas duas condições. 1) Você está passando uma entrada que pode ser anulável. Um exemplo é um nome de arquivo opcional. 2) Se você deseja denunciar a propriedade. Como se você passar ou devolver o ponteiro, você não tem nenhuma cópia restante nem use o ponteiro que você distribui

ptr=blah; func(ptr); //never use ptr again for here on out.

Mas não utilizo ponteiros ou ponteiros inteligentes há muito tempo e criei um perfil do meu aplicativo. Corre muito rápido.

NOTA ADICIONAL: Percebo que escrevo minhas próprias estruturas e as repito. Então, como faço isso sem usar ponteiros? Não é um contêiner STL, então passar por ref é lento. Eu sempre carrego minha lista de dados / deques / mapas e tal. Não me lembro de retornar nenhum objeto, a menos que fosse algum tipo de lista / mapa. Nem mesmo uma corda. Eu olhei o código para objetos únicos e percebo que faço algo assim, { MyStruct v; func(v, someinput); ... } void func(MyStruct&v, const D&someinput) { fillV; }então praticamente retorno objetos (vários) ou pré-aloco / passo em uma referência para preencher (único).

Agora, se você estava escrevendo, deque, map, etc, precisará usar ponteiros. Mas você não precisa. Vamos STL e, possivelmente, aumentar a preocupação com isso. Você só precisa escrever dados e soluções. Não recipientes para segurá-los;)

Espero que você nunca use ponteiros: D. Boa sorte ao lidar com bibliotecas que o forçam a

user2528
fonte
0

Os ponteiros são muito úteis para trabalhar com dispositivos mapeados na memória. Você pode definir uma estrutura que reflete (digamos) um registro de controle, depois atribuí-lo ao endereço do registro de controle real na memória e manipulá-lo diretamente. Você também pode apontar diretamente para um buffer de transferência em um cartão ou chip se a MMU o mapear no espaço de memória do sistema.

TMN
fonte
0

Eu vejo ponteiros como meu dedo indicador, usamos para fazer algumas coisas:

  1. quando alguém pede algo que você não pode carregar, como onde fica a rua, eu "apontaria" para essa rua, e é isso que fazemos no caso de argumentos por referência
  2. ao contar ou percorrer algo, usaríamos o mesmo dedo, e é isso que fazemos nas matrizes

por favor, perdoe esta má resposta

A.Rashad
fonte
0

Como sua pergunta está marcada como C ++, eu responderei sua pergunta para esse idioma.

No C ++, há uma distinção entre ponteiros e referências; portanto, existem dois cenários em que ponteiros (ou ponteiros inteligentes) são necessários para facilitar determinados comportamentos. Eles podem ser usados ​​em outras circunstâncias, no entanto, você pergunta como "melhor usá-los" e em todas as outras circunstâncias existem alternativas melhores.

1. Polimorfismo

Um ponteiro de classe base permite chamar um método virtual que depende do tipo de objeto para o qual o ponteiro aponta.

2. Criando objetos persistentes

Ponteiros são necessários ao criar um objeto dinamicamente (na pilha em vez da pilha). Isso é necessário quando você deseja que a vida útil dos objetos seja maior que o escopo em que é criado.

Em termos de "bons projetos ou problemas a serem resolvidos", como já foi dito aqui, qualquer projeto não trivial fará uso de ponteiros.

dangerousdave
fonte
Talvez alguém prefira cortar objetos do que usar ponteiro. Feliz depuração: D
deadalnix 13/09/11