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?
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?
Respostas:
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.
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:
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.
Listas vinculadas
Você pode armazenar o próximo e / ou local do item anterior e não todos os dados associados ao registro.
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:
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 .
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:
Encontre alguns projetos que usam herança.
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.
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 foo
sai 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 malloc
aloca 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 ++ new
e new[]
executam a mesma função que malloc
. Os operadores delete
e delete[]
são análogos a free
. Embora new
e delete
deva ser usado exclusivamente para alocar e liberar objetos C ++, o uso malloc
e free
é perfeitamente legal no código C ++.
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:
Eu recomendo o último.
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:
selected_object
que é uma referência a um dos objetos é muito mais eficiente do que copiar o valor do objeto atual em uma nova variável.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.
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)
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?).
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.
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?"
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:
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.
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).
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.
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
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.
Eu vejo ponteiros como meu dedo indicador, usamos para fazer algumas coisas:
por favor, perdoe esta má resposta
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.
Um ponteiro de classe base permite chamar um método virtual que depende do tipo de objeto para o qual o ponteiro aponta.
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.