A partir do número de perguntas postadas aqui, fica claro que as pessoas têm alguns problemas bastante fundamentais ao se orientar sobre indicadores e aritmética dos indicadores.
Estou curioso para saber o porquê. Eles nunca me causaram grandes problemas (embora eu tenha aprendido sobre eles no Neolítico). Para escrever melhores respostas para essas perguntas, gostaria de saber o que as pessoas acham difícil.
Então, se você está lutando com ponteiros, ou recentemente, mas de repente "entendeu", quais foram os aspectos dos ponteiros que lhe causaram problemas?
Respostas:
Eu suspeito que as pessoas estão se aprofundando um pouco em suas respostas. Não é realmente necessário um entendimento de agendamento, operações reais da CPU ou gerenciamento de memória no nível de assembly.
Quando estava ensinando, descobri que os seguintes buracos no entendimento dos alunos eram a fonte mais comum de problemas:
A maioria dos meus alunos conseguiu entender um desenho simplificado de um pedaço de memória, geralmente a seção de variáveis locais da pilha no escopo atual. Geralmente, dar endereços fictícios explícitos aos vários locais ajudou.
Acho que, em resumo, estou dizendo que, se você deseja entender os ponteiros, precisa entender as variáveis e o que elas realmente são nas arquiteturas modernas.
fonte
Quando comecei a trabalhar com eles, o maior problema que tive foi a sintaxe.
são todos iguais.
mas:
Por quê? porque a parte "ponteiro" da declaração pertence à variável e não ao tipo
E, em seguida, desreferenciar a coisa usa uma notação muito semelhante:
Exceto quando você realmente precisa obter um ponteiro ... então você usa um e comercial!
Viva a consistência!
Então, aparentemente, para serem idiotas e provar como são inteligentes, muitos desenvolvedores de bibliotecas usam ponteiros para ponteiros para ponteiros, e se esperam uma variedade dessas coisas, por que não passar um ponteiro para isso também? .
Para chamar isso, preciso do endereço da matriz de ponteiros para ponteiros para ponteiros de ints:
Em seis meses, quando tiver que manter esse código, passarei mais tempo tentando descobrir o que tudo isso significa do que reescrever do zero. (sim, provavelmente entendi errado a sintaxe - já faz um tempo desde que eu fiz alguma coisa em C. Eu meio que sinto falta disso, mas depois sou um massoquista)
fonte
O entendimento adequado dos ponteiros requer conhecimento sobre a arquitetura da máquina subjacente.
Atualmente, muitos programadores não sabem como suas máquinas funcionam, assim como a maioria das pessoas que sabe dirigir um carro não sabe nada sobre o motor.
fonte
Ao lidar com indicadores, as pessoas que ficam confusas estão amplamente em um dos dois campos. Eu estive (sou?) Em ambos.
A
array[]
multidãoEssa é a multidão que não sabe traduzir de notação de ponteiro para notação de matriz (ou nem sabe que eles estão relacionados). Aqui estão quatro maneiras de acessar elementos de uma matriz:
A idéia aqui é que acessar matrizes por meio de ponteiros parece bastante simples e direto, mas muitas coisas muito complicadas e inteligentes podem ser feitas dessa maneira. Alguns dos quais podem deixar programadores experientes em C / C ++ perplexos, muito menos novatos inexperientes.
O
reference to a pointer
epointer to a pointer
multidãoEste é um ótimo artigo que explica a diferença e que citarei e roubarei algum código de :)
Como um pequeno exemplo, pode ser muito difícil ver exatamente o que o autor deseja fazer se você se deparar com algo assim:
Ou, em menor grau, algo como isto:
Então, no final do dia, o que realmente resolvemos com toda essa bobagem? Nada.
Essa complexidade e a aparente (aparente negritude) permutabilidade com referências, que geralmente é outra advertência de ponteiros e um erro dos recém-chegados, dificulta a compreensão dos ponteiros. Também é importante entender, para fins de conclusão, que indicadores de referências são ilegais em C e C ++ por motivos confusos que levam você a
lvalue
-rvalue
semântica.Como uma resposta anterior comentou, muitas vezes você só tem esses programadores que pensam que estão sendo espertos ao usar
******awesome_var->lol_im_so_clever()
e a maioria de nós provavelmente é culpada de escrever essas atrocidades às vezes, mas isso não é um bom código e certamente não é sustentável .Bem, esta resposta acabou por ser mais longa do que eu esperava ...
fonte
Culpo a qualidade dos materiais de referência e das pessoas que ensinam pessoalmente; a maioria dos conceitos em C (mas especialmente os indicadores) são simplesmente mal ensinados . Continuo ameaçando escrever meu próprio livro em C (intitulado A última coisa que o mundo precisa é outro livro sobre a linguagem de programação C ), mas não tenho tempo nem paciência para fazê-lo. Então eu saio aqui e jogo citações aleatórias do Standard nas pessoas.
Também existe o fato de que, quando o C foi projetado inicialmente, presumiu-se que você entendesse a arquitetura da máquina com um nível bastante detalhado, apenas porque não havia como evitá-la no seu trabalho cotidiano (a memória era tão estreita e os processadores eram tão lentos você tinha que entender como o que você escreveu afetou o desempenho).
fonte
Há um ótimo artigo que apóia a noção de que os indicadores são difíceis no site de Joel Spolsky - The Perils of JavaSchools .
[Isenção de responsabilidade - eu não sou um odiador de Java por si só .]
fonte
A maioria das coisas é mais difícil de entender se você não está fundamentado no conhecimento que está "embaixo". Quando eu ensinei CS, ficou muito mais fácil quando comecei meus alunos a programar uma "máquina" muito simples, um computador decimal simulado com códigos de operação decimais cuja memória consistia em registros decimais e endereços decimais. Eles colocariam programas muito curtos para, por exemplo, adicionar uma série de números para obter um total. Depois, eles passavam para assistir ao que estava acontecendo. Eles poderiam manter pressionada a tecla "Enter" e vê-la correr "rápido".
Tenho certeza que quase todo mundo no SO se pergunta por que é útil ser tão básico. Esquecemos como era não saber programar. Brincar com um computador de brinquedo cria conceitos sem os quais você não pode programar, como as idéias de que a computação é um processo passo a passo, usando um pequeno número de primitivas básicas para criar programas e o conceito de memória variáveis como locais onde os números são armazenados, nos quais o endereço ou o nome da variável é diferente do número que ela contém. Há uma distinção entre a hora em que você entra no programa e a hora em que "é executado". Eu comparo aprender a programar como atravessar uma série de "lombadas", como programas muito simples, depois loops e sub-rotinas, matrizes, E / S sequenciais, ponteiros e estrutura de dados.
Finalmente, ao chegar a C, os ponteiros são confusos, embora a K&R tenha feito um ótimo trabalho em explicá-los. A maneira como os aprendi em C foi saber como lê-los - da direita para a esquerda. Como quando vejo
int *p
na minha cabeça, digo "p
aponta para umint
". C foi inventado como um passo à frente da linguagem assembly e é disso que eu gosto - é próximo desse "terreno". Ponteiros, como qualquer outra coisa, são mais difíceis de entender se você não tiver esse fundamento.fonte
Não recebi dicas até ler a descrição em K&R. Até aquele momento, os ponteiros não faziam sentido. Eu li um monte de coisas onde as pessoas diziam "Não aprenda dicas, elas são confusas e machucam sua cabeça e causam aneurismas", então eu me esquivei disso por um longo tempo e criei esse ar desnecessário de conceito difícil .
Caso contrário, principalmente o que eu pensava ser, por que diabos você desejaria uma variável que você precisa passar por aros para obter o valor e, se você quisesse atribuir coisas a ela, teria que fazer coisas estranhas para obter valores? neles. Todo o ponto de uma variável é algo para armazenar um valor, pensei, então por que alguém queria complicar estava além de mim. "Então, com um ponteiro, você precisa usar o
*
operador para obter seu valor ??? Que tipo de variável pateta é essa?" , Eu pensei. Inútil, sem trocadilhos.A razão pela qual foi complicado foi porque eu não entendi que um ponteiro era um endereço para alguma coisa. Se você explicar que é um endereço, que é algo que contém um endereço para outra coisa e que você pode manipular esse endereço para fazer coisas úteis, acho que isso pode esclarecer a confusão.
Uma classe que exigia o uso de ponteiros para acessar / modificar portas em um PC, usando a aritmética de ponteiros para abordar diferentes localizações de memória, e olhando para o código C mais complicado que modificava seus argumentos me desapontou da ideia de que os ponteiros eram inúteis.
fonte
Aqui está um exemplo de ponteiro / matriz que me deu uma pausa. Suponha que você tenha duas matrizes:
E seu objetivo é copiar o conteúdo do uint8_t do destino de origem usando memcpy (). Adivinhe quais das seguintes ações atingem esse objetivo:
A resposta (alerta de spoiler!) São TODOS eles. "destination", "& destination" e "& destination [0]" são todos do mesmo valor. "& destination" é um tipo diferente dos outros dois, mas ainda é o mesmo valor. O mesmo vale para as permutações de "fonte".
Como um aparte, eu pessoalmente prefiro a primeira versão.
fonte
sizeof(source)
, porque sesource
for um ponteiro,sizeof
não será o que você deseja. Às vezes (nem sempre) escrevosizeof(source[0]) * number_of_elements_of_source
apenas para ficar longe desse bug.Eu deveria começar dizendo que C e C ++ foram as primeiras linguagens de programação que aprendi. Comecei com C, depois fiz C ++ na escola, muito, e depois voltei para C para me tornar fluente.
A primeira coisa que me confundiu sobre ponteiros ao aprender C foi a simples:
Essa confusão estava principalmente enraizada em ter sido introduzida no uso de referência a uma variável para argumentos OUT antes que os ponteiros fossem corretamente introduzidos para mim. Lembro-me de que deixei de escrever os primeiros exemplos em C para Dummies, porque eram muito simples para nunca conseguir que o primeiro programa que escrevi funcionasse (provavelmente por causa disso).
O que era confuso sobre isso era o que
&ch
realmente significava e também porquestr
não precisava.Depois que me familiarizei com isso, lembro-me de estar confuso sobre a alocação dinâmica. Eu percebi em algum momento que ter ponteiros para dados não era extremamente útil sem a alocação dinâmica de algum tipo, então escrevi algo como:
para tentar alocar dinamicamente algum espaço. Não deu certo. Eu não tinha certeza de que iria funcionar, mas não sabia de que outra forma poderia funcionar.
Mais tarde eu aprendi sobre
malloc
enew
, mas eles realmente pareciam geradores de memória mágicos para mim. Eu não sabia nada sobre como eles poderiam funcionar.Algum tempo depois, eu estava aprendendo a recursão novamente (eu já havia aprendido antes, mas agora estava na aula) e perguntei como funcionava sob o capô - onde estavam armazenadas as variáveis separadas. Meu professor disse "na pilha" e muitas coisas ficaram claras para mim. Eu já tinha ouvido o termo antes e implementado pilhas de software antes. Eu já tinha ouvido outros se referirem à "pilha" muito antes, mas havia esquecido.
Nessa época, também percebi que o uso de matrizes multidimensionais em C pode ficar muito confuso. Eu sabia como eles funcionavam, mas eles eram tão fáceis de se envolver, que eu decidi tentar contorná-los sempre que pudesse. Eu acho que o problema aqui foi principalmente sintático (especialmente passando ou devolvendo-os de funções).
Desde que eu escrevi C ++ para a escola nos próximos dois anos, adquiri muita experiência usando ponteiros para estruturas de dados. Aqui eu tive um novo conjunto de problemas - misturando indicadores. Eu teria vários níveis de indicadores (coisas como
node ***ptr;
) me enganando. Desdiferenciaria um ponteiro o número errado de vezes e, eventualmente, recorria a descobrir quantas*
eu precisava por tentativa e erro.Em algum momento, aprendi como o heap de um programa funcionava (mais ou menos, mas bom o suficiente para não me manter acordado à noite). Lembro-me de ler que, se você olhar alguns bytes antes do ponteiro que
malloc
em um determinado sistema retornar, poderá ver quantos dados foram realmente alocados. Percebi que o códigomalloc
poderia pedir mais memória do sistema operacional e essa memória não fazia parte dos meus arquivos executáveis. Ter uma idéia decente de comomalloc
funciona é realmente útil.Logo depois disso, participei de uma aula de montagem, que não me ensinou tanto sobre ponteiros como a maioria dos programadores provavelmente pensa. Isso me fez pensar mais sobre em qual assembly meu código poderia ser traduzido. Eu sempre tentei escrever código eficiente, mas agora tinha uma idéia melhor de como fazê-lo.
Também participei de algumas aulas nas quais tive que escrever um cocô . Ao escrever lisp, eu não estava tão preocupado com a eficiência quanto em C. Eu tinha muito pouca idéia de como esse código poderia ser traduzido se compilado, mas eu sabia que parecia usar muitos símbolos nomeados locais (variáveis) criados coisas muito mais fáceis. Em algum momento, eu escrevi um pouco de código de rotação de árvore AVL em um pouco de lisp, que tive muita dificuldade em escrever em C ++ por causa de problemas com ponteiros. Percebi que minha aversão ao que eu pensava serem variáveis locais em excesso havia prejudicado minha capacidade de escrever esse e vários outros programas em C ++.
Também participei de uma aula de compiladores. Enquanto nesta aula, avancei para o material avançado e aprendi sobre atribuição única estática (SSA) e variáveis mortas, o que não é tão importante, exceto que me ensinou que qualquer compilador decente fará um trabalho decente ao lidar com variáveis que são não mais usado. Eu já sabia que mais variáveis (incluindo ponteiros) com tipos corretos e bons nomes me ajudariam a manter as coisas em mente, mas agora também sabia que evitá-las por razões de eficiência era ainda mais estúpido do que meus professores menos otimistas disseram mim.
Então, para mim, saber um pouco sobre o layout da memória de um programa ajudou muito. Pensar no que meu código significa, simbolicamente e no hardware, me ajuda. O uso de ponteiros locais com o tipo correto ajuda bastante. Costumo escrever um código que se parece com:
de modo que, se eu estragar um tipo de ponteiro, é muito claro pelo erro do compilador qual é o problema. Se eu fiz:
e com algum tipo de ponteiro errado, o erro do compilador seria muito mais difícil de descobrir. Eu ficaria tentado a recorrer a mudanças de tentativa e erro na minha frustração e provavelmente pioraria as coisas.
fonte
Olhando para trás, havia quatro coisas que realmente me ajudaram a entender os ponteiros. Antes disso, eu podia usá-los, mas não os entendia completamente. Ou seja, eu sabia que, se seguisse os formulários, obteria os resultados desejados, mas não entendia completamente o "porquê" dos formulários. Sei que isso não é exatamente o que você pediu, mas acho que é um corolário útil.
Escrevendo uma rotina que levou um ponteiro para um número inteiro e modificou o número inteiro. Isso me deu as formas necessárias para construir quaisquer modelos mentais de como os ponteiros funcionam.
Alocação de memória dinâmica unidimensional. Descobrir a alocação de memória 1-D me fez entender o conceito do ponteiro.
Alocação de memória dinâmica bidimensional. Descobrir a alocação de memória 2-D reforçou esse conceito, mas também me ensinou que o ponteiro em si requer armazenamento e deve ser levado em consideração.
Diferenças entre variáveis de pilha, variáveis globais e memória de pilha. Descobrir essas diferenças me ensinou os tipos de memória aos quais os ponteiros apontam / se referem.
Cada um desses itens exigia imaginar o que estava acontecendo em um nível mais baixo - a construção de um modelo mental que satisfizesse todos os casos que eu pensasse em jogar nele. Demorou tempo e esforço, mas valeu a pena. Estou convencido de que, para entender os ponteiros, é preciso construir esse modelo mental sobre como eles funcionam e como são implementados.
Agora, de volta à sua pergunta original. Com base na lista anterior, houve vários itens que tive dificuldade em entender originalmente.
fonte
Eu tive meu "momento ponteiro" trabalhando em alguns programas de telefonia em C. Eu tive que escrever um emulador de intercâmbio AXE10 usando um analisador de protocolo que só compreendesse o C. clássico. Tudo dependia de conhecer ponteiros. Tentei escrever meu código sem eles (ei, eu era "pré-apontador" me deu uma folga) e falhei completamente.
A chave para entendê-los, para mim, era o operador & (endereço). Depois que entendi que
&i
significava o "endereço de i", entendi que*i
significava "o conteúdo do endereço apontado por i" veio um pouco mais tarde. Sempre que escrevia ou lia meu código, repetia sempre o que "&" significava e o que "*" significava, e acabei por usá-los intuitivamente.Para minha vergonha, fui forçado a usar o VB e o Java, para que meu conhecimento sobre ponteiros não seja tão nítido quanto antes, mas estou feliz por ser "pós-ponteiro". Não me peça para usar uma biblioteca que exija que eu compreenda * * p.
fonte
&i
é o endereço e*i
o conteúdo, o que éi
?A principal dificuldade com ponteiros, pelo menos para mim, é que não comecei com C. Comecei com Java. Toda a noção de ponteiros era realmente estranha até algumas aulas na faculdade, onde eu esperava conhecer C. Então, eu me ensinei o básico de C e como usar ponteiros em seu sentido muito básico. Mesmo assim, toda vez que me vejo lendo o código C, tenho que procurar na sintaxe do ponteiro.
Portanto, em minha experiência muito limitada (1 ano no mundo real + 4 na faculdade), os indicadores me confundem porque eu nunca tive que usá-lo realmente em outra coisa senão em sala de aula. E posso simpatizar com os alunos que agora começam o CS com JAVA em vez de C ou C ++. Como você disse, você aprendeu dicas na era 'Neolítica' e provavelmente a usa desde então. Para nós, pessoas novas, a noção de alocar memória e fazer aritmética de ponteiros é realmente estranha, porque todas essas linguagens abstraíram isso.
PS Depois de ler o ensaio de Spolsky, sua descrição de 'JavaSchools' não se parecia em nada com o que eu passei na faculdade em Cornell ('05 a 09). Tomei as estruturas e a programação funcional (sml), sistemas operacionais (C), algoritmos (caneta e papel) e várias outras classes que não eram ensinadas em java. No entanto, todas as classes de introdução e eletivas foram todas feitas em java porque é importante não reinventar a roda quando você está tentando fazer algo mais nivelado do que implementar uma hashtable com ponteiros.
fonte
void foo(Clazz obj) { obj = new Clazz(); }
é um não-op, enquantovoid bar(Clazz obj) { obj.quux = new Quux(); }
se transforma o argumento ...Aqui está uma não resposta: Use cdecl (ou c ++ decl) para descobrir:
fonte
Eles adicionam uma dimensão extra ao código sem uma alteração significativa na sintaxe. Pense sobre isso:
Só há uma coisa a alteração:
a
. Você pode escrevera = 6
e os resultados são óbvios para a maioria das pessoas. Mas agora considere:Há duas coisas
a
relevantes em momentos diferentes: o valor real dea
, o ponteiro e o valor "por trás" do ponteiro. Você pode mudara
:... e
some_int
ainda está em algum lugar com o mesmo valor. Mas você também pode alterar o que ele aponta:Há uma lacuna conceitual entre eles
a = 6
, que tem apenas efeitos colaterais locais e*a = 6
que pode afetar várias outras coisas em outros lugares. Meu argumento aqui não é que o conceito de indireção seja inerentemente complicado, mas porque você pode fazer tanto a coisa imediata e locala
quanto a indireta com*a
... isso pode ser o que confunde as pessoas.fonte
Eu tinha programado em c ++ por 2 anos e depois convertido para Java (5 anos) e nunca mais olhei para trás. No entanto, quando recentemente tive que usar algumas coisas nativas, descobri (com espanto) que não havia esquecido nada sobre ponteiros e até as acho fáceis de usar. Este é um forte contraste com o que experimentei 7 anos atrás, quando tentei entender o conceito. Então, acho que entender e gostar é uma questão de maturidade de programação? :)
OU
Os ponteiros são como andar de bicicleta; depois que você descobre como trabalhar com eles, não há como esquecer.
Em suma, difícil de entender ou não, toda a idéia do ponteiro é MUITO educacional e acredito que deve ser entendida por todo programador, independentemente de ele programar em um idioma com ponteiros ou não.
fonte
Os ponteiros são difíceis por causa da indireção.
fonte
Ponteiros (junto com alguns outros aspectos do trabalho de baixo nível) exigem que o usuário tire a mágica.
A maioria dos programadores de alto nível gosta da mágica.
fonte
Ponteiros são uma maneira de lidar com a diferença entre um identificador para um objeto e um objeto em si. (ok, não necessariamente objetos, mas você sabe o que eu quero dizer, bem como a minha mente)
Em algum momento, você provavelmente terá que lidar com a diferença entre os dois. Na linguagem moderna e de alto nível, isso se torna a distinção entre copiar por valor e copiar por referência. De qualquer forma, é um conceito que muitas vezes é difícil para os programadores entenderem.
No entanto, como foi apontado, a sintaxe para lidar com esse problema em C é feia, inconsistente e confusa. Eventualmente, se você realmente tentar entender, um ponteiro fará sentido. Mas quando você começa a lidar com ponteiros para ponteiros, e assim por diante ad nauseum, fica realmente confuso para mim e para outras pessoas.
Outra coisa importante a lembrar sobre os ponteiros é que eles são perigosos. C é a linguagem de um programador mestre. Pressupõe que você saiba o que diabos está fazendo e, assim, lhe dá o poder de realmente estragar as coisas. Embora alguns tipos de programas ainda precisem ser escritos em C, a maioria dos programas não precisa, e se você tiver uma linguagem que forneça uma melhor abstração para a diferença entre um objeto e seu identificador, sugiro que você o use.
De fato, em muitos aplicativos C ++ modernos, geralmente ocorre que qualquer aritmética de ponteiro necessária seja encapsulada e abstraída. Não queremos que os desenvolvedores façam aritmética de ponteiros em todo o lugar. Queremos uma API centralizada e bem testada que faça a aritmética dos ponteiros no nível mais baixo. A alteração desse código deve ser feita com muito cuidado e testes extensivos.
fonte
Penso que uma das razões pelas quais os ponteiros C são difíceis é que eles conflitam vários conceitos que não são realmente equivalentes; No entanto, como todos eles são implementados usando ponteiros, as pessoas podem ter dificuldade em desembaraçar os conceitos.
Em C, os ponteiros estão acostumados, entre outras coisas:
Em C, você definiria uma lista vinculada de números inteiros como este:
O ponteiro está lá apenas porque esta é a única maneira de definir uma estrutura de dados recursiva em C, quando o conceito realmente não tem nada a ver com detalhes de baixo nível como endereços de memória. Considere o seguinte equivalente em Haskell, que não requer o uso de ponteiros:
Bem direto - uma lista está vazia ou é formada a partir de um valor e do restante da lista.
Veja como você pode aplicar uma função
foo
a todos os caracteres de uma string em C:Apesar de também usar um ponteiro como iterador, este exemplo tem muito pouco em comum com o anterior. Criar um iterador que você pode incrementar é um conceito diferente de definir uma estrutura de dados recursiva. Nenhum dos conceitos está especialmente ligado à idéia de um endereço de memória.
Aqui está uma assinatura de função real encontrada em glib :
Uau! Isso é um bocado de
void*
's. E é apenas para declarar uma função que itera sobre uma lista que pode conter qualquer tipo de coisa, aplicando uma função a cada membro. Compare com comomap
é declarado em Haskell:Isso é muito mais direto:
map
é uma função que pega uma função que converte ana
em ab
e a aplica a uma lista dea
's' para produzir uma lista deb
's. Assim como na função Cg_list_foreach
,map
não precisa saber nada em sua própria definição sobre os tipos aos quais será aplicada.Resumindo:
Eu acho que os ponteiros C seriam muito menos confusos se as pessoas aprendessem sobre estruturas de dados recursivas, iteradores, polimorfismo etc. como conceitos separados e depois aprendessem como os ponteiros podem ser usados para implementar essas idéias em C , em vez de mascarar todas essas conceitos juntos em um único assunto de "ponteiros".
fonte
c != NULL
no seu exemplo "Olá, mundo" ... você quer dizer*c != '\0'
.Eu acho que requer uma base sólida, provavelmente do nível da máquina, com a introdução de alguns códigos de máquina, montagem e como representar itens e estrutura de dados na RAM. Demora um pouco de tempo, algum trabalho de casa ou prática de resolução de problemas e algum pensamento.
Mas se uma pessoa conhece linguagens de alto nível a princípio (o que não está errado - um carpinteiro usa um machado. Uma pessoa que precisa dividir átomo usa outra coisa. Precisamos de pessoas que são carpinteiros e temos pessoas que estudam átomos) e essa pessoa que conhece a linguagem de alto nível recebe uma introdução de 2 minutos aos ponteiros e, em seguida, é difícil esperar que ele entenda a aritmética de ponteiros, ponteiros para ponteiros, matriz de ponteiros para cadeias de tamanho variável e matriz de matriz de caracteres, etc. Uma base sólida de baixo nível pode ajudar muito.
fonte
O problema que sempre tive (principalmente autodidata) é o "quando" usar um ponteiro. Posso envolver minha sintaxe na construção de um ponteiro, mas preciso saber sob quais circunstâncias um ponteiro deve ser usado.
Eu sou o único com essa mentalidade? ;-)
fonte
Era uma vez ... Tínhamos microprocessadores de 8 bits e todos escreviam em montagem. A maioria dos processadores incluía algum tipo de endereçamento indireto usado para tabelas e kernels de salto. Quando as linguagens de nível superior surgiram, adicionamos uma fina camada de abstração e as denominamos ponteiros. Ao longo dos anos, nos afastamos cada vez mais do hardware. Isto não é necessariamente uma coisa ruim. Eles são chamados de idiomas de nível superior por um motivo. Quanto mais eu puder me concentrar no que quero fazer, em vez de nos detalhes de como isso é feito, melhor.
fonte
Parece que muitos estudantes têm um problema com o conceito de indireção, principalmente quando encontram o conceito de indireção pela primeira vez. Lembro-me de quando eu era um estudante que, dos mais de 100 alunos do meu curso, apenas um punhado de pessoas realmente entendeu as dicas.
O conceito de indireção não é algo que costumamos usar na vida real e, portanto, é um conceito difícil de entender inicialmente.
fonte
Recentemente, tive apenas o momento do clique do ponteiro e fiquei surpreso por achar isso confuso. Era mais que todo mundo falava tanto sobre isso, que eu assumi que alguma magia negra estava acontecendo.
O jeito que eu entendi foi isso. Imagine que todas as variáveis definidas recebam espaço de memória em tempo de compilação (na pilha). Se você deseja um programa capaz de lidar com grandes arquivos de dados, como áudio ou imagens, não deseja uma quantidade fixa de memória para essas estruturas em potencial. Então, você espera até o tempo de execução para atribuir uma certa quantidade de memória para armazenar esses dados (na pilha).
Depois de ter seus dados na memória, você não deseja copiar esses dados em todo o barramento de memória toda vez que desejar executar uma operação nele. Digamos que você queira aplicar um filtro aos seus dados de imagem. Você tem um ponteiro que inicia na frente dos dados que você atribuiu à imagem e uma função percorre esses dados, alterando-os no lugar. Se você não soubesse o que estamos fazendo, provavelmente acabaria criando duplicatas de dados à medida que a executava na operação.
Pelo menos é assim que eu vejo no momento!
fonte
Falando como um novato em C ++ aqui:
O sistema de ponteiros demorou um pouco para eu digerir não necessariamente por causa do conceito, mas por causa da sintaxe C ++ relativa ao Java. Algumas coisas que achei confusas são:
(1) Declaração variável:
vs.
vs.
e aparentemente
é uma declaração de função e não uma declaração de variável. Em outros idiomas, há basicamente apenas uma maneira de declarar uma variável.
(2) O e comercial é usado de algumas maneiras diferentes. Se for
então o & a é um endereço de memória.
OTOH, se for
então o & a é um parâmetro passado por referência.
Embora isso possa parecer trivial, pode ser confuso para novos usuários - eu vim do Java e o Java é uma linguagem com um uso mais uniforme dos operadores
(3) Relação matriz-ponteiro
Uma coisa que é um pouco frustrante de entender é que um ponteiro
pode ser um ponteiro para um int
ou
pode ser uma matriz para um int
E apenas para deixar as coisas mais confusas, ponteiros e matriz não são intercambiáveis em todos os casos e ponteiros não podem ser transmitidos como parâmetros da matriz.
Isso resume algumas das frustrações básicas que tive com o C / C ++ e seus indicadores, que a IMO é bastante composta pelo fato de o C / C ++ ter todas essas peculiaridades específicas da linguagem.
fonte
Eu, pessoalmente, não entendi o ponteiro, mesmo depois da minha pós-graduação e depois do meu primeiro emprego. A única coisa que eu sabia é que você precisa disso para lista vinculada, árvores binárias e para passar matrizes para funções. Esta foi a situação, mesmo no meu primeiro emprego. Somente quando comecei a dar entrevistas, entendi que o conceito de ponteiro é profundo e tem um tremendo uso e potencial. Então comecei a ler K & R e a escrever o próprio programa de teste. Todo o meu objetivo era orientado para o trabalho.
Nesse momento, descobri que os indicadores não são realmente ruins nem difíceis se forem ensinados de uma maneira boa. Infelizmente, quando eu aprendi C na graduação, o professor não estava ciente do ponteiro, e até as tarefas estavam usando menos ponteiros. No nível de pós-graduação, o uso do ponteiro é realmente apenas criar árvores binárias e lista vinculada. Esse pensamento de que você não precisa entender adequadamente os ponteiros para trabalhar com eles, acaba com a ideia de aprendê-los.
fonte
Ponteiros .. hah .. tudo sobre ponteiro na minha cabeça é que ele fornece um endereço de memória em que os valores reais sejam qual for sua referência .. então não há mágica nisso .. se você aprender alguma montagem, não terá muito problema em aprender como funciona o ponteiro .. vamos lá pessoal ... mesmo em Java tudo é uma referência ..
fonte
O principal problema que as pessoas não entendem por que precisam de indicadores. Porque eles não são claros sobre pilha e pilha. É bom começar a partir do assembler de 16 bits para x86 com modo de memória minúsculo. Ajudou muitas pessoas a ter idéia da pilha, pilha e "endereço". E byte :) Os programadores modernos às vezes não conseguem dizer quantos bytes você precisa para endereçar o espaço de 32 bits. Como eles podem ter uma idéia dos ponteiros?
O segundo momento é a notação: você declara o ponteiro como *, obtém o endereço como & e isso não é fácil de entender para algumas pessoas.
E a última coisa que vi foi um problema de armazenamento: eles entendem a pilha e a pilha, mas não conseguem entrar na ideia de "estática".
fonte