Por que o mestre C, Dennis Ritchie, introduziu indicadores em C? E por que as outras linguagens de programação como VB.NET ou Java ou C # as eliminaram? Encontrei alguns pontos no Google e também quero ouvir seus comentários. Por que eles estão eliminando conceitos de ponteiros nas linguagens modernas?
As pessoas dizem que C é a linguagem básica e ponteiros é o conceito que torna C poderoso e excelente e ainda faz C competir com idiomas mais modernos. Então, por que eles eliminaram indicadores em idiomas mais modernos?
Você acha que o conhecimento de ponteiros ainda é importante para novos programadores? Atualmente, as pessoas estão usando VB.NET ou Java, que suporta recursos mais avançados do que C (e não usa conceitos de ponteiros) e muitas pessoas como eu vejo agora (meus amigos) escolhem essas linguagens ignorando C, pois suportam recursos avançados. Eu digo a eles para começarem com C. Eles dizem que é um desperdício aprender os conceitos de ponteiros quando você está fazendo coisas avançadas em VB.NET ou Java que não são possíveis em C.
O que você acha?
Atualizado :
Os comentários que li no Google são:
Os computadores anteriores eram muito lentos e não otimizados.
O uso de ponteiros torna possível acessar um endereço diretamente e isso economiza tempo, em vez de fazer uma cópia dele em chamadas de função.
A segurança é significativamente pior usando ponteiros, e é por isso que Java e C # não os incluíram.
Estes e mais um pouco do que encontrei. Eu ainda preciso de algumas respostas valiosas. Isso iria ser incrivelmente apreciado.
Respostas:
Naquela época, os desenvolvedores estavam trabalhando muito mais próximos do metal. C era essencialmente uma substituição de nível superior para montagem, que é quase o mais próximo possível do hardware, por isso era natural que você precisasse de indicadores para ser eficiente na solução de problemas de codificação. No entanto, os ponteiros são ferramentas afiadas, que podem causar grandes danos se usadas de forma descuidada. Além disso, o uso direto de ponteiros abre a possibilidade de muitos problemas de segurança, que não eram um problema na época (em 1970, a Internet consistia em cerca de algumas dúzias de máquinas em algumas universidades, e nem era chamada assim. ...), mas tornou-se cada vez mais importante desde então. Atualmente, linguagens de nível superior são projetadas conscientemente para evitar indicadores de memória não processada.
Dizer que "coisas avançadas feitas em VB.Net ou Java não são possíveis em C" mostra um ponto de vista muito limitado, para dizer o mínimo :-)
Antes de tudo, todas essas linguagens (inclusive montagem) são Turing completas, portanto, em teoria, o que for possível em um idioma, é possível em todos. Apenas pense no que acontece quando um pedaço de código VB.Net ou Java é compilado e executado: eventualmente, é traduzido (ou mapeado para) código de máquina, porque essa é a única coisa que a máquina entende. Em linguagens compiladas como C e C ++, é possível obter todo o corpo do código de máquina equivalente ao código fonte original de nível superior, como um ou mais arquivos / bibliotecas executáveis. Em linguagens baseadas em VM, é mais complicado (e talvez nem seja possível) obter toda a representação equivalente de código de máquina do seu programa, mas ainda assim ela está presente em algum lugar, dentro dos recessos profundos do sistema de tempo de execução e do JIT.
Agora, é claro, é uma questão totalmente diferente se alguma solução é viável em um idioma específico. Nenhum desenvolvedor sensato começaria a escrever um aplicativo Web no assembly :-) Mas é útil ter em mente que a maioria ou todas essas linguagens de nível superior são criadas com base em uma enorme quantidade de código de tempo de execução e de biblioteca de classes, uma grande parte da que é implementado em um idioma de nível inferior, normalmente em C.
Então, para chegar à pergunta,
O conceito por trás dos ponteiros é indireto . Este é um conceito muito importante e IMHO todo bom programador deve entender em um determinado nível. Mesmo se alguém estiver trabalhando apenas com idiomas de nível superior, a indireção e as referências ainda serão importantes. Não compreender isso significa ser incapaz de usar toda uma classe de ferramentas muito potentes, limitando seriamente a capacidade de resolução de problemas a longo prazo.
Portanto, minha resposta é sim, se você quiser se tornar um programador realmente bom, também deve entender os ponteiros (assim como a recursão - esse é o outro obstáculo típico para desenvolvedores iniciantes). Você pode não precisar começar com isso - não acho que o C seja ótimo como primeira língua hoje em dia. Mas em algum momento deve-se familiarizar com a indireção. Sem ele, nunca podemos entender como as ferramentas, bibliotecas e estruturas que estamos usando realmente funcionam. E um artesão que não entende como suas ferramentas funcionam é muito limitado. Justo o suficiente, pode-se entender também em linguagens de programação de nível superior. Um bom teste decisivo está implementando corretamente uma lista duplamente vinculada - se você pode fazê-lo no seu idioma favorito, pode afirmar que entende bem a indireção.
Mas, se não fosse por mais alguma coisa, deveríamos fazê-lo para aprender a respeitar os programadores antigos que conseguiram construir coisas inacreditáveis usando as ferramentas ridiculamente simples que possuíam (em comparação com o que temos agora). Estamos todos de pé sobre os ombros dos gigantes, e é bom para nós reconhecer isso, em vez de fingir que somos os próprios gigantes.
fonte
Eu acho que você precisa diferir.
Java e outras linguagens de nível superior não removeram ponteiros. O que eles fizeram foi remover a aritmética simples do ponteiro.
De fato, Java ainda permite uma aritmética de ponteiro protegido e restrito : o acesso ao array. No C antigo, o acesso ao array nada mais é do que desreferenciamento. É uma notação diferente, um açúcar sintático, se você quiser, para comunicar claramente o que está fazendo.
Ainda assim,
array[index]
é equivalente a*(array+index)
. Por isso, também é equivalente a,index[array]
embora eu suponha que alguns compiladores C possam lhe dar um aviso, se você fizer isso.Como corolário,
pointer[0]
é equivalente a*pointer
. Isso é simplesmente porque o "ponteiro para uma matriz" é o endereço da primeira entrada da matriz e os endereços dos elementos subseqüentes são calculados adicionando o índice.Em Java, a aritmética simples de ponteiro (referência e desreferenciação) não existe mais. No entanto, existem indicadores. Eles os chamam de referências, mas isso não muda o que é. E o acesso ao array ainda é exatamente o mesmo: verifique o endereço, adicione o índice e use esse local de memória. No entanto, em Java, ele verificará se esse índice está ou não dentro dos limites da matriz que você alocou originalmente. Caso contrário, lançará uma exceção.
Agora, a vantagem da abordagem Java é que você não possui código, que grava cegamente bytes arbitrários em locais de memória arbitrários. Isso melhora a segurança e também a segurança, porque se você não verificar os estouros de buffer, o tempo de execução fará isso por você.
A desvantagem disso é que é simplesmente menos poderoso. É possível fazer uma programação segura de memória em C. É impossível se beneficiar da velocidade e das possibilidades de programação insegura em Java.
Na verdade, não há nada difícil sobre ponteiros ou aritmética de ponteiros. Eles normalmente são explicados de maneira complicada, enquanto tudo que um ponteiro é é um índice para uma matriz gigante (seu espaço de memória), tudo que faz referência a um valor fornece o índice onde encontrá-lo, tudo o que a desreferenciação é procurar o valor em um determinado índice. (Isso é um pouco simplificado, porque não leva em conta que os valores têm tamanho diferente na memória, dependendo do tipo. Mas isso é um detalhe circunstancial, e não uma parte do conceito real)
IMHO, todos em nosso trabalho devem ser capazes de entender isso, ou eles estão simplesmente no campo errado.
fonte
back2dos
estava certo na primeira vez, já que(array + index)
já leva em conta o tamanho dos objetos (em C).array[index]
, e é isso*(array+index)
. Se você quiser mostrar como o compilador faz as coisas internamente, é possível falar explicitamente sobre bytes ou fornecer o assembly.O conceito de ponteiros é importante no conhecimento geral da programação de computadores. Compreender o conceito é bom para programadores ou programadores de qualquer idioma, mesmo que o idioma não o suporte diretamente.
Os ponteiros têm seu uso nas estruturas de dados (listas vinculadas) e no design do banco de dados (chave estrangeira).
Idiomas como VB e C # podem passar dados por "referência" a métodos, que podem ser considerados como um tipo de ponteiro.
Compreender onde os dados são alocados na memória (pilha x pilha) ainda é importante para a eficiência dos algoritmos.
Aprender o básico direito é importante na minha opinião.
fonte
std::sort
,std::partition
,std::find
, Etc. Eles trabalham com ponteiros, e eles trabalham com objetos que agem como ponteiros (iterators). E eles trabalham sobre qualquer coleção geral; listas vinculadas, matrizes dinâmicas,deque
s, árvores ou qualquer outro tipo de coleção definida pelo usuário. Você não pode esse tipo de abstração sem ponteiros.Sim, sim, sim, sim e sim !!!
Se você não souber o básico, NUNCA será capaz de resolver os problemas realmente difíceis, estranhos, difíceis e complicados que surgirem no seu caminho.
E se você entende muito bem o básico, é MUITO mais comercializável no mercado de trabalho.
Trabalhei uma vez com um sujeito que programava há 10 anos e não fazia ideia de como os ponteiros funcionavam. Eu (muito mais júnior) passei horas em um quadro branco educando-o. Isso abriu meus olhos. Ele não tinha idéia de tantas coisas básicas.
Saiba o máximo que puder.
fonte
Sim, a compreensão é importante.
Alguns meses atrás, eu estava programando em C # e queria fazer uma cópia de uma lista. Claro que o que fiz foi
NewList = OldList;
e depois comecei a modificarNewList
. Quando tentei imprimir as duas listas, elas eram as mesmas, já queNewList
eram apenas um ponteiro paraOldList
e não uma cópia, então eu estava realmente mudando o tempoOldList
todo. Não demorei muito para descobrir isso, mas alguns dos meus colegas de classe não foram tão rápidos e tiveram que ser explicados por que isso está acontecendo.Exemplo:
E, claro, o resultado é assim:
Saber usá-los não é tão importante, mas entendê-los é crucial!
fonte
NewList
era apenas um ponteiro paraOldList
" - para ser preciso, ambosNewList
eOldList
eram apenas ponteiros, referindo-se ao mesmoList
objeto.b
não tem mais referências a ela e agora é lixo.Ponteiro do conceito! = Ponteiro da aritmética! = Ponteiro da sintaxe
O primeiro importa sempre, se você precisa (e precisa) de uma cópia profunda / superficial, passa por referência / passa por valor, etc. Os outros dois importam apenas se o seu idioma do dia permitir que você os use.
fonte
a=[1,2]; b=a; a.append(3)
que tantoa
eb
vão ambos referenciar o mesmo objeto[1,2,3]
sem saber coisas como em C oi
elemento th de uma matriz pode ser referenciado porarr[i]
oui[arr]
como ambos são*(arr+i)
. Eu prefiro quando o idioma não deixai[arr]
ser usado.Porque os ponteiros são um mecanismo muito poderoso que pode ser usado de várias maneiras.
Porque os ponteiros são um mecanismo muito perigoso que pode ser mal utilizado de várias maneiras.
Eu acho que os programadores devem aprender sobre indicadores, mas de uma perspectiva educacional, não é aconselhável apresentá-los cedo. A razão é que eles são usados para diversos fins, é difícil dizer como iniciante por que você está usando um ponteiro em uma circunstância específica.
Aqui está uma lista incompleta para que os ponteiros são usados:
new T
)struct T { T* next; /* ... */ };
)for (T* p = &a[0]; p != &a[0] + n; ++p) { ... }
)T* new_pointer = existing_pointer;
)T* pointer_to_base = pointer_to_derived;
)mutate(&object);
)if (p) { /* ... */ }
)Observe que o uso de um único mecanismo para todos esses conceitos demonstra o poder e a elegância do programador experiente e o grande potencial de confusão para alguém novo em programação.
fonte
Por quê? Você pode escrever um sistema enorme com designer de formulários e gerador de código. Não é suficiente? (ironia)
E agora, falando sério, os indicadores não são parte crucial da programação em muitas áreas, mas permitem que as pessoas entendam como os internos funcionam. E se não tivermos ninguém que entenda como funcionam os internos, haverá uma situação em que SQL2020, Windows 15 e Linux 20.04 serão gravados em uma máquina virtual coletada de lixo executando mais de 30 camadas de abstração, com código gerado via IDE, em JavaScript .
Definitivamente não é o que eu quero ver.
Então, sim, eles precisam, definitivamente!
fonte
Nem Java nem C # eliminaram ponteiros, eles têm referências quase iguais. O que foi eliminado é a aritmética dos ponteiros, que pode ser omitida em um curso introdutório.
Nenhuma aplicação não trivial poderia ser feita sem o conceito de ponteiros ou referências, portanto vale a pena ensinar (nenhuma alocação dinâmica de memória poderia ser feita sem eles).
Considere o seguinte em C ++ e Java, e acho que não é muito diferente em C #:
aClass *x = new aClass();
aClass x = new aClass();
não há realmente muita diferença entre ponteiros e referências, certo?
A aritmética dos ponteiros deve ser evitada, a menos que seja necessário e ao programar com modelos de alto nível, para que não haja muito problema.
fonte
O programador profissional deve dominar os indicadores.
As pessoas que desejam conhecer programação devem aprender sobre sua existência e implicações, mas não necessariamente usá-las.
As pessoas que desejam resolver problemas pessoais via programação (como eu, que usam muitos scripts Python) podem muito bem ignorá-los.
Bem, essa é a minha opinião ...; o)
fonte
Ponteiros de endereço variável são um caso específico do conceito mais generalizado de indireção. O indireto é usado na maioria dos idiomas (todos?) Modernos em muitas construções, como delegados e retornos de chamada. Compreender o conceito de indireção permite saber quando e como usar melhor essas ferramentas.
fonte
Absufreakinglutely SIM ! Todo mundo que programa precisa entender ponteiros e indiretos.
Os indicadores são como uma grande quantidade de acesso a dados é realizada em todos os idiomas. Ponteiros são um recurso de hardware de todos os microprocessadores. Linguagens de alto nível, como Java, VB e C #, essencialmente impedem o acesso direto aos ponteiros dos usuários da linguagem com referências. As referências se referem a objetos por meio do esquema de gerenciamento de memória do idioma (pode ser um ponteiro com metadados ou apenas um número para uma tabela de memória, por exemplo).
Compreender como os ponteiros funcionam é fundamental para entender como os computadores realmente funcionam. Os ponteiros também são mais flexíveis e poderosos que as referências.
Por exemplo, a razão pela qual matrizes começam no índice zero é porque elas são, na verdade, atalhos para a aritmética do ponteiro. Sem aprender como os ponteiros funcionam, muitos programadores iniciantes não conseguem arrays.
A linha 2 na aritmética do ponteiro seria:
Sem entender os ponteiros, não se pode entender o gerenciamento de memória, a pilha, a pilha ou mesmo matrizes! Além disso, é preciso entender ponteiros e desreferenciamento para entender como as funções e os objetos são transmitidos.
TL: DR : entender os ponteiros é fundamental para entender como os computadores realmente funcionam .
fonte
Eu acho que tudo se resume ao fato de que a necessidade de lidar com ponteiros desapareceu à medida que os programadores lidavam menos com o hardware direto em que estavam executando. Por exemplo, alocar uma estrutura de dados de lista vinculada de forma que caiba perfeitamente na sequência de módulos de memória de 640 bytes que o hardware especializado possuía.
Lidar com ponteiros manualmente pode ser propenso a erros (levando a vazamentos de memória e código explorável) e é demorado para acertar. Portanto, Java e C # etc agora gerenciam sua memória e seus ponteiros para você através de suas Máquinas Virtuais (VMs). Isso é sem dúvida menos eficiente do que usar C / C ++ bruto, embora as VMs estejam melhorando constantemente.
C (e C ++) ainda são linguagens amplamente usadas, especialmente nos espaços de computação de alto desempenho, jogos e hardware incorporado. Pessoalmente, sou grato por ter aprendido sobre ponteiros, pois a transição para as referências do Java (um conceito semelhante aos ponteiros) foi muito fácil e não fiquei perdida quando vi meu primeiro NullPointerException (que realmente deveria ser chamado de NullReferenceException, mas discordo). .
Eu recomendaria aprender sobre o conceito de ponteiros, pois eles ainda sustentam muitas estruturas de dados, etc. Em seguida, escolha uma linguagem na qual você goste de trabalhar, sabendo que, se algo como um NPE aparecer, você saberá o que realmente está acontecendo. .
fonte
Esta é a verdade objetiva:
Alguns idiomas suportam acesso direto à memória (ponteiros), outros não. Existem boas razões para cada caso.
Como alguém disse aqui, nos dias de C, o gerenciamento automático de memória não era tão elaborado quanto é hoje. E as pessoas estavam acostumadas a isso, de qualquer maneira. Os bons programadores da época tinham um entendimento muito mais profundo dos programas de computador do que da nossa geração (tenho 21 anos). Eles estão usando cartões perfurados e dias de espera há algum tempo de compilação no mainframe. Eles provavelmente sabiam por que cada parte do código existia.
A vantagem óbvia de linguagens como C é que elas permitem que você tenha um controle mais preciso sobre seu programa. Quando você realmente precisa hoje em dia? Somente quando você estiver criando aplicativos de infraestrutura, como programas relacionados ao SO e ambientes de tempo de execução. Se você deseja apenas desenvolver um software bom, rápido, robusto e confiável, o gerenciamento automático de memória costuma ser sua melhor escolha.
O fato é que o acesso direto à memória foi abusado principalmente ao longo do histórico de desenvolvimento de software. As pessoas estavam criando programas que vazavam memória e eram mais lentas por causa da alocação de memória redundante (em C é fácil e comum estender o espaço de memória virtual do processo para cada alocação).
Atualmente, máquinas virtuais / tempos de execução fazem um trabalho muito melhor do que 99% dos programadores na alocação e liberação de memória. Além disso, eles permitem flexibilidade extra no fluxo que você deseja que seu programa tenha, porque você (em sua maioria) não está ocupado com a liberação de memória alocada no local e hora certos.
Quanto ao conhecimento. Eu acho admirável que os programadores saibam como os ambientes nos quais eles programam são implementados. Não necessariamente nos mínimos detalhes, mas no quadro geral.
Eu acho que saber como os ponteiros funcionam (no mínimo) é interessante. O mesmo que saber como o polimorfismo é implementado. De onde seu processo obtém sua memória e como. Essas são coisas que sempre me interessaram pessoalmente. Posso dizer honestamente que eles me tornaram um programador melhor, mas não posso dizer que eles são uma necessidade educacional para quem quer se tornar um bom programador. Em qualquer um dos casos, saber mais geralmente o tornará melhor no seu trabalho.
Porque, mesmo que você não conheça todos os pequenos detalhes, alguém que o faça poderá mudar o que você criou para algo que simplesmente tenha um desempenho melhor. E isso geralmente não é um trabalho difícil, uma vez que você tem um design adequado, limpo e testável (e isso geralmente é a maior parte do trabalho).
Se eu fosse um entrevistador procurando contratar alguém para um aplicativo de idioma de alto nível, essas seriam as coisas pelas quais eu estaria mais interessado.
Conhecimento de baixo nível é um bônus. É bom para depuração e, ocasionalmente, para criar soluções um pouco melhores. Faz de você uma pessoa interessante, profissionalmente. Concede-lhe algum respeito no seu local de trabalho.
Mas no mundo de hoje, não é um requisito sagrado.
fonte
Para propósitos mais práticos em linguagens OO de alto nível, é necessário entender as referências. Você realmente não precisa entender como essas linguagens implementam as referências em termos de ponteiros.
Existem abordagens multi-paradigma muito mais funcionais e modernas que eu valorizaria muito mais do que ser capaz de fazer aritmética de ponteiro sofisticada para dizer, escreva a 1000ª função de cópia de string otimizada provavelmente com desempenho pior que String.copy da sua biblioteca std de qualquer maneira.
Aconselho que você aprenda muito mais conceitos diferentes e de nível superior primeiro, e comece a aprender linguagens de projetos diferentes para ampliar seu horizonte antes de tentar se especializar em coisas próximas ao hardware.
Frequentemente, vejo tentativas totalmente fracassadas de otimizar o servlet da Web ou código semelhante para obter um ganho de 5%, quando o cache (memorização), otimização de SQL ou apenas o ajuste da configuração do servidor da web pode render 100% ou mais com pouco esforço. Brincar com ponteiros é uma otimização prematura na maioria dos casos.
fonte
Definitivamente, precisamos ter um conceito completo de ponteiro, se você realmente deseja ser um bom programador. O motivo do conceito de ponteiro foi um acesso direto ao seu valor, que se torna mais eficiente e eficaz com restrições de tempo ...
Além disso, hoje em dia, considerando os aplicativos móveis, que têm memória muito limitada, precisamos usá-lo com muito cuidado para que sua operação seja muito rápida com a resposta do usuário ... Portanto, para esse fim, precisamos de uma referência direta ao valor ...
Considere os dispositivos da Apple, ou a linguagem Objective C, que funciona totalmente apenas com o conceito de ponteiro. Toda a variável declarada no Objetivo C está tendo Ponteiro. Você precisa acessar o wiki do Objective C
fonte