As mentes jovens precisam aprender os conceitos de ponteiro?

89

Por que o mestre C Dennis Ritchie introduziu ponteiros em C? E por que as outras linguagens de programação, como VB.NET, Java ou C #, as eliminaram? Eu encontrei alguns pontos no Google e quero ouvir seus comentários também. Por que eles estão eliminando os conceitos de ponteiro nas linguagens modernas?

As pessoas dizem que C é a linguagem básica e os ponteiros é o conceito que torna o C poderoso e excelente e faz o C ainda competir com linguagens mais modernas. Então, por que eles eliminaram ponteiros em idiomas mais modernos?

Você acha que o conhecimento de ponteiros ainda é importante para novos programadores? As pessoas estão usando VB.NET ou Java atualmente, que suporta recursos mais avançados que C (e não usa nenhum conceito de ponteiro) e muitas pessoas como eu vejo agora (meus amigos) escolhem essas linguagens ignorando C, já que elas 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 as 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 leio no Google são:

  1. Os computadores anteriores eram muito lentos e não otimizados.

  2. O uso de ponteiros possibilita acessar um endereço diretamente e isso economiza tempo em vez de fazer uma cópia dele em chamadas de função.

  3. A segurança é significativamente pior usando ponteiros, e é por isso que Java e C # não os incluíram.

Estes e mais alguns que eu encontrei. Eu ainda preciso de algumas respostas valiosas. Isso seria muito apreciado.

    
por niko 05.09.2011 / 22:50
fonte

16 respostas

127

Naquela época, os desenvolvedores estavam trabalhando muito mais perto do metal. C era essencialmente um substituto de nível mais alto para a montagem, que é quase tão próximo do hardware quanto possível, então era natural que você precisasse de ponteiros 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 usados de maneira descuidada. Além disso, o uso direto de ponteiros abre a possibilidade para muitos problemas de segurança, que não eram um problema naquela época (em 1970, a internet consistia em cerca de algumas dezenas de máquinas em algumas universidades, e nem sequer era chamada assim ...), mas tornou-se cada vez mais importante desde então. Portanto, hoje em dia, linguagens de alto nível são projetadas conscientemente para evitar ponteiros de memória brutos.

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: -)

Em primeiro lugar, todas essas linguagens (até mesmo assembly) são Turing completas, portanto, em teoria, o que for possível em um idioma, é possível em todos. Basta pensar sobre o que acontece quando uma parte do código VB.Net ou Java é compilada e executada: eventualmente, ela é traduzida em (ou mapeada para) código de máquina, porque essa é a única coisa que a máquina entende. Em linguagens compiladas como C e C ++, você pode obter o corpo inteiro do código de máquina equivalente ao código fonte de nível superior original, como um ou mais arquivos / bibliotecas executáveis. Em linguagens baseadas em VM, é mais complicado (e pode nem ser possível) obter toda a representação equivalente do código de máquina do seu programa, mas ainda assim ele está lá em algum lugar, dentro dos recessos profundos do sistema de tempo de execução e do JIT. / p>

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 em assembly :-) Mas é útil ter em mente que a maioria ou todas essas linguagens de nível mais alto são construídas em cima de uma grande quantidade de tempo de execução e código de biblioteca de classes, um grande pedaço de que é implementado em uma linguagem de nível inferior, normalmente em C.

Então, para chegar à pergunta,

Do you think knowledge on pointers to the young people [...] is important?

O conceito por trás dos ponteiros é indireto . Este é um conceito muito importante e IMHO todo bom programador deve compreendê-lo em um determinado nível. Mesmo que alguém trabalhe apenas com linguagens de nível superior, a indireção e as referências ainda são importantes. Não entender isso significa ser incapaz de usar uma classe inteira de ferramentas muito potentes, limitando seriamente a capacidade de resolver problemas a longo prazo.

Então, minha resposta é sim, se você quer se tornar um programador realmente bom, você deve entender os ponteiros também (assim como a recursão - este é o outro típico obstáculo para desenvolvedores iniciantes). Você pode não precisar começar com isso - eu não acho que C é ideal como primeira língua hoje em dia. Mas, em algum momento, deve-se familiarizar-se com a indireção. Sem isso, nunca podemos entender como as ferramentas, bibliotecas e frameworks que estamos usando realmente funcionam. E um artesão que não entende como suas ferramentas funcionam é muito limitado. Justo o suficiente, pode-se ter uma noção disso em linguagens de programação de nível superior também. Um bom teste decisivo é a correta implementação de uma lista duplamente vinculada - se você pode fazê-lo em seu idioma favorito, pode reivindicar que entende bem a indireção.

Mas se não for para mais nada, devemos fazê-lo para aprender o respeito pelos programadores de antigamente que conseguiram construir coisas inacreditáveis usando as ferramentas ridiculamente simples que tinham (comparadas 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 gigantes.

    
por 05.09.2011 / 12:29
fonte
38

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 de ponteiro simples.

Na verdade, o Java ainda permite uma aritmética de ponteiro protegido e restrito : o acesso à matriz. No antigo C simples, o acesso à matriz não é nada além de desreferenciação. É uma notação diferente, um açúcar sintático, se você quiser, para comunicar claramente o que você está fazendo.
Ainda assim, array[index] é equivalente a *(array+index) . Por causa disso, também é equivalente a index[array] , embora eu suponha que alguns compiladores C possam lhe dar um aviso, se você fizer isso.
Como um 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 pela adição do índice.

Em Java, as aritméticas de ponteiro simples (referenciamento e desreferência) não existem mais. No entanto ponteiros existem. Eles chamam de referências, mas isso não muda o que é. E o acesso à matriz ainda é exatamente a mesma coisa: observe o endereço, adicione o índice e use a localização da memória. No entanto, em Java, ele verificará se esse índice está ou não dentro dos limites da matriz originalmente alocada. Caso contrário, lançará uma exceção.

Agora, a vantagem da abordagem Java é que você não tem código, que apenas grava cegamente bytes arbitrários em locais de memória arbitrária. Isso melhora a segurança e também a segurança, porque se você não conseguir verificar o estouro de buffer e tal, o tempo de execução fará isso por você.

A desvantagem disso é que é simplesmente menos poderoso. É possível fazer programação segura em 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 são normalmente explicados de maneira complicada, enquanto que tudo que um ponteiro é, é um índice para um array gigante (seu espaço de memória), todos referenciando um valor é dando o índice onde encontrá-lo, tudo o que a desreferenciação faz é procurar o valor em um determinado índice. (Isto é apenas um pouco simplificado, porque não leva em conta que os valores são de tamanho diferente na memória, dependendo do seu tipo. Mas isso é um detalhe circunstancial, em vez de uma parte do conceito real)

IMHO, todo mundo em nosso trabalho deve ser capaz de entender isso, ou eles estão simplesmente no campo errado.

    
por 05.09.2011 / 13:08
fonte
24

O conceito de ponteiros é importante no corpo de conhecimento geral de programação de computadores. Entender o conceito é bom para futuros programadores ou programadores de qualquer idioma, mesmo que o idioma não o suporte diretamente.

Os ponteiros têm seu uso em estruturas de dados (listas vinculadas) e no design do banco de dados (chave estrangeira).

Linguagens como VB e C # podem passar dados por "referência" a métodos, que podem ser considerados como um tipo de ponteiro.

Entender onde os dados são alocados na memória (pilha vs. heap) ainda é importante para a eficiência dos algoritmos.

Aprender o básico é importante na minha opinião.

    
por 05.09.2011 / 11:42
fonte
19

Sim, sim, sim, sim e sim !!!

Se você não sabe o básico, NUNCA será capaz de resolver os problemas realmente difíceis, estranhos, difíceis e complicados que surgem em seu caminho.

E se você entender o básico muito bem, será MUITO mais comercializável no mercado de trabalho.

Eu trabalhei uma vez com um cara que tinha programado por 10 anos, e não tinha ideia de como os ponteiros funcionavam. Eu (muito mais júnior) passava horas em um quadro branco educando-o. Isso abriu meus olhos. Ele não tinha idéia sobre tantas coisas básicas.

Saiba o quanto você puder.

    
por 05.09.2011 / 12:54
fonte
17

Sim, entender é importante.

Alguns meses atrás eu estava programando em C #, e queria fazer uma cópia de uma lista. Claro que o que eu fiz foi NewList = OldList; e então comecei a modificar NewList . Quando tentei imprimir ambas as listas, elas eram as mesmas, pois NewList era apenas um ponteiro para OldList e não uma cópia, então eu estava realmente alterando OldList o tempo todo. Não demorei muito tempo para descobrir isso, mas alguns dos meus colegas não foram tão rápidos e tiveram que ser explicados por que isso está acontecendo.

Exemplo:

List<int> a = new List<int>();
a.Add(2);
a.Add(9);
a.Add(8);
a.Add(1);
List<int> b = new List<int>();
b = a; //Does not make a copy, b is just a synonym!
b.Sort();
for (int i = 0; i < a.Count; i++)
{
    Console.WriteLine("a: " + a[i] + " b: " + b[i]);
}

E, claro, o resultado é assim:

a: 1 b: 1
a: 2 b: 2
a: 8 b: 8
a: 9 b: 9

Saber como usá-los não é tão importante, mas compreendê-los é crucial!

    
por 05.09.2011 / 23:06
fonte
14

Ponteiro do conceito! = Ponteiro da aritmética! = Ponteiro da sintaxe

O primeiro sempre importa, se você precisa (e você faz) compreensão de cópia profunda / superficial, passar por referência / passar por valor, etc. Os outros dois só importam se o seu idioma du jour permitir que você os use. / p>     

por 05.09.2011 / 15:25
fonte
14

Why did the C master Dennis Ritchie introduce pointers in C?

Porque os ponteiros são um mecanismo muito poderoso que pode ser usado de várias maneiras.

And why did the other programming languages like VB.NET or Java or C# eliminate them?

Porque os ponteiros são um mecanismo muito perigoso que pode ser usado de várias maneiras.

Eu acho que programadores deveriam aprender sobre ponteiros, mas de uma perspectiva educacional, não é aconselhável introduzi-los cedo. A razão é que eles são usados para muitos propósitos diferentes, é difícil dizer como um novato por que você está usando um ponteiro em uma circunstância particular.

Aqui está uma lista incompleta para quais ponteiros são usados:

  • alocação dinâmica ( new T )
  • estruturas de dados recursivas ( struct T { T* next; /* ... */ }; )
  • iteradores sobre matrizes ( for (T* p = &a[0]; p != &a[0] + n; ++p) { ... } )
  • acesso compartilhado a objetos ( T* new_pointer = existing_pointer; )
  • Polimorfismo de subtipo
  • ( T* pointer_to_base = pointer_to_derived; )
  • chamada herdada por referência ( mutate(&object); )
  • tipos opcionais ( if (p) { /* ... */ } )

Note que usar um único mecanismo para todos esses conceitos demonstra tanto o poder e a elegância para o programador experiente quanto o grande potencial de confusão para alguém novo em programação.

    
por 05.09.2011 / 23:06
fonte
12

Por quê? Você pode escrever um sistema enorme com designer de formulários e gerador de código. Não é suficiente? (ironia)

E agora, seriamente, 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 os internos funcionam, haverá uma situação em que o SQL2020, o Windows 15 e o Linux 20.04 serão gravados em uma máquina virtual com coleta de lixo em mais de 30 camadas de abstração, com código gerado via IDE, em JavaScript .

Definitivamente não é isso que eu quero ver.

Então, sim, eles definitivamente precisam!

    
por 05.09.2011 / 12:12
fonte
7

Nem Java nem C # eliminaram ponteiros, eles têm referências quase iguais. O que foi eliminado é a aritmética de ponteiro, 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, então vale a pena ensinar (Nenhuma alocação de memória dinâmica 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 de ponteiro deve ser evitada, a menos que seja necessária, e ao programar com modelos de alto nível, portanto não há muito problema lá.

    
por 05.09.2011 / 15:41
fonte
6

O programador profissional deve dominar os ponteiros.

As pessoas que querem saber programação devem aprender sobre sua existência e implicações, mas não necessariamente usá-las.

As pessoas que querem resolver problemas pessoais por meio de programação (como eu, que usam muitos scripts Python) poderiam ignorá-las.

Bem, essa é a minha opinião ...; o)

    
por 05.09.2011 / 15:03
fonte
3

Ponteiros de endereço variáveis são um caso específico do conceito mais generalizado de indirecção. Indirection é usado na maioria (todas?) Linguagens modernas em muitos constructos como delegados e callbacks. Compreender o conceito de indirecção permite-lhe saber quando e como utilizar melhor estas ferramentas.

    
por 05.09.2011 / 15:17
fonte
3

Absufreakinglutely SIM ! Todos que programam precisam entender ponteiros e indireções.

Os indicadores são como uma grande quantidade de acesso a dados é feita em todos os idiomas. Ponteiros são um recurso de hardware de todos os microprocessadores. Idiomas de alto nível como Java, VB & C # essencialmente impede o acesso direto aos ponteiros dos usuários da linguagem com referências. Referências referem-se a objetos através do esquema de gerenciamento de memória da linguagem (pode ser um ponteiro com metadados ou apenas um número para a tabela de memória, por exemplo).

Entender 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 as matrizes começam no índice zero é porque as matrizes são realmente abreviadas para a aritmética de ponteiros. Sem aprender sobre como os ponteiros funcionam, muitos programadores iniciantes não conseguem arrays.

int a, foo[10];
foo[2] = a;

A linha 2 na aritmética de ponteiros seria:

*(foo + sizeof(int) * 2) = a;

Sem entender os ponteiros, não é possível entender o gerenciamento de memória, a pilha, o heap ou até mesmo os arrays! Além disso, é necessário entender ponteiros e desreferenciação para entender como funções e objetos são passados.

TL: DR : entender os ponteiros é fundamental para entender os computadores que realmente funcionam .

    
por 06.09.2011 / 10:02
fonte
2

Eu acho que isso se resume ao fato de que a necessidade de lidar com ponteiros caiu quando os programadores lidaram menos com o hardware direto em que estavam rodando. Por exemplo, alocar uma estrutura de dados de lista vinculada de uma maneira que se encaixe perfeitamente na sequência de módulos de memória de 640 bytes que o hardware especializado tinha.

Lidar com ponteiros manualmente pode ser propenso a erros (levando a vazamentos de memória e código explorável) e consome tempo para acertar. Então Java e C # etc agora todos gerenciam sua memória e seus indicadores para você através de suas Máquinas Virtuais (VMs). Isso é possivelmente menos eficiente do que usar o C / C ++ bruto, embora as VMs estejam melhorando constantemente.

C (e C ++) ainda são idiomas amplamente utilizados, especialmente nos espaços de hardware de computação de alto desempenho, jogos e embarcados. Pessoalmente, agradeço ter aprendido sobre os ponteiros, pois a transição para as referências do Java (um conceito semelhante aos ponteiros) foi muito fácil e não me perdi quando vi meu primeiro NullPointerException (que deve ser chamado de NullReferenceException, mas eu discordo) .

Eu aconselharia aprender sobre o conceito de ponteiros, pois eles ainda sustentam muitas estruturas de dados, etc. Depois, escolha uma linguagem que você goste de trabalhar, sabendo que, se algo como um NPE surgir, você sabe o que é realmente acontecendo.

    
por 05.09.2011 / 11:45
fonte
0

Esta é a verdade objetiva:

Alguns idiomas suportam acesso direto à memória (ponteiros), outros não. Há boas razões para cada caso.

  1. 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 com isso, de qualquer forma. Os bons programadores da época tinham uma compreensão muito mais profunda dos programas de computador do que da nossa geração (tenho 21 anos). Eles têm usado cartões perfurados e dias de espera por algum tempo de compilação no mainframe. Eles provavelmente sabiam porque todos os códigos existentes existiam.

  2. A vantagem óbvia de linguagens como C é que elas permitem que você tenha um melhor controle sobre seu programa. Quando você realmente precisa , nos dias de hoje? Somente quando você está criando aplicativos de infra-estrutura, como programas relacionados ao sistema operacional e ambientes de tempo de execução. Se você deseja desenvolver um software bom, rápido, robusto e confiável, o gerenciamento automático de memória é, na maioria das vezes, sua melhor escolha.

  3. O fato é que o acesso direto à memória foi abusado principalmente no curso do histórico de desenvolvimento de software. As pessoas criaram programas que vazaram memória e foram mais lentas devido à 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 única).

  4. Atualmente, as 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 uma flexibilidade extra no fluxo que você deseja que seu programa tenha, porque você (na maioria das vezes) não está ocupado com a liberação de memória alocada no momento e lugar certos.

  5. Quanto ao conhecimento. Eu acho que é admirável que os programadores saibam como os ambientes que 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 foram de interesse para mim, pessoalmente. Posso dizer honestamente que eles me tornaram um programador melhor, mas não posso dizer que eles sejam uma necessidade educacional para quem quer se tornar um bom programador. Em qualquer um dos casos, saber mais frequentemente vai torná-lo melhor em seu trabalho.

  1. A meu ver, se tudo o que você precisa fazer é criar um aplicativo em Java ou C # ou algo assim, então você precisa se concentrar em técnicas adequadas de design e implementação. Código testável, código limpo, código flexível. Nessa ordem.

Porque, mesmo que você não conheça todos os pequenos detalhes, alguém que fizer isso poderá mudar o que você criou em algo que simplesmente funcionará melhor. E isso geralmente não é um trabalho difícil, uma vez que você tenha um design adequado, limpo e testável no local (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 em que 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. Isso faz de você uma pessoa interessante, profissionalmente. Isso lhe garante algum respeito em seu local de trabalho.

Mas no mundo de hoje, não é um requisito sagrado.

    
por 11.09.2011 / 20:52
fonte
-1

Para propósitos mais práticos em linguagens OO de alto nível, entender as referências é suficiente, mas você não precisa realmente entender como essas linguagens implementam referências em termos de ponteiros.

Existem muito mais abordagens multi-paradigmáticas funcionais e modernas que eu valorizo muito mais do que poder fazer aritmética de ponteiro de fantasia para dizer, escreva a 1000ª função de cópia de string otimizada provavelmente com um desempenho pior do que String.copy de seu std lib de qualquer maneira.

Eu aconselharia aprender conceitos muito mais diferentes e de nível superior primeiro, além de aprender idiomas de diferentes designs para ampliar seu horizonte antes de tentar se especializar em material próximo ao hardware.

Muitas vezes, vejo tentativas fracassadas de micro-otimização de servlet da web ou código semelhante para ganho de 5%, quando o cache (memoization), otimização de SQL ou apenas ajuste de configuração do servidor web pode render 100% ou mais com pouco esforço. Brincando com ponteiros é otimização prematura na maioria dos casos.

    
por 05.09.2011 / 12:35
fonte
-1

Definitivamente, precisamos ter um conceito completo de ponteiro, se você realmente quiser ser um bom programador. A razão para o conceito do ponteiro foi um acesso direto ao seu valor, que se torna mais eficiente e eficaz com restrições de tempo ...

Além disso, agora, considerando 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 ... Assim, para isso, precisamos de uma referência direta ao valor ...

Considere os dispositivos da Apple, ou a linguagem Objective C, que está totalmente trabalhando apenas com o conceito de ponteiro. Toda a variável declarada no Objetivo C está tendo Pointer. Você precisa passar pelo wiki do Objective C

    
por 11.09.2011 / 19:06
fonte