Pergunta

AKA -? O que é esta obsessão com ponteiros

Tendo apenas realmente utilizado, orientadas a objeto linguagens modernas como ActionScript, Java e C #, eu realmente não entendo a importância de ponteiros eo que você usá-los para. O que estou perdendo aqui?

Foi útil?

Solução

É tudo apenas indireto: A capacidade de não lidar com dados, mas dizer "eu vou encaminhá-lo para alguns dados, por lá". Você tem o mesmo conceito em Java e C #, mas apenas em formato de referência.

As principais diferenças são que as referências são sinais efetivamente imutáveis ??- eles sempre apontam para alguma coisa. Isso é útil e fácil de entender, mas menos flexível do que o modelo de ponteiro C. ponteiros C são sinais de que você pode feliz reescrever. Você sabe que a seqüência que você está procurando ao lado da corda que está sendo apontada? Bem, basta alterar ligeiramente a sinalização.

Este casais bem com de C "perto do osso, o conhecimento de baixo nível necessário" abordagem. Nós sei que uma char* foo consiste em um conjunto de caracteres começando no local apontado pelo letreiro foo. Se nós também sabemos que a corda é de pelo menos 10 caracteres, podemos mudar o sinal para (foo + 5) ao ponto em seguida, mesma cadeia, mas comece a metade do comprimento em.

Esta flexibilidade é útil quando você sabe o que está fazendo, e morte se não o fizer (em que "sabe" é mais do que apenas "conhecer a linguagem", é "conhecer o estado exato do programa"). Obtê-lo errado, e seu poste de sinalização está direcionando-o para fora da borda de um penhasco. Referências não deixá-lo mexer, então você está muito mais confiante de que você pode segui-los sem riscos (especialmente quando combinada com regras como "Um objeto referenciado nunca vai desaparecer", como na maioria dos idiomas recolhidos lixo).

Outras dicas

Você está perdendo um monte! Entender como o computador funciona em níveis mais baixos é muito útil em várias situações. C e assembler vai fazer isso por você.

Basicamente um ponteiro permite que você escreva coisas para qualquer ponto na memória do computador. No mais primitivo hardware / OS ou em sistemas embarcados isso realmente pode fazer algo útil. Say virar as blinkenlichts e fora novamente.

É claro que isso não funciona em sistemas modernos. O sistema operacional é o Senhor e Mestre de memória principal. Se você tentar acessar um local de memória errado, o seu processo vai pagar por sua arrogância com a sua vida.

Em C, os ponteiros são a maneira de passar referências aos dados. Quando você chamar uma função, você não deseja copiar um milhão de bits a uma pilha. Em vez disso você só dizer onde os dados residem na memória principal. Em outras palavras, você dá um ponteiro para os dados.

Até certo ponto isso é o que acontece mesmo com Java. Você passa referências a objetos, não a si mesmos objetos. Lembre-se, em última análise, cada objeto é um conjunto de bits na memória principal do computador.

Os ponteiros são para manipular diretamente o conteúdo da memória.

É até você se você acha que isso é uma boa coisa a fazer, mas é a base de como tudo é feito em C ou assembler.

linguagens de alto nível esconder ponteiros nos bastidores: por exemplo, uma referência em Java é implementada como um ponteiro em quase qualquer JVM você vai se deparar, razão pela qual ele é chamado NullPointerException em vez de NullReferenceException. Mas não deixe que o programador acessar diretamente o endereço de memória que aponta para, e não pode ser modificada para ter um valor diferente do que o endereço de um objeto do tipo correto. Por isso, não oferecem a mesma potência (e responsabilidade) que ponteiros em linguagens de baixo nível fazem.

[Edit: esta é uma resposta para a pergunta 'o que é esta obsessão com ponteiros?'. Tudo o que eu comparação é ponteiros assembler / C-estilo com referências Java. O título da pergunta tem mudado desde: tinha me propus a responder à nova pergunta eu poderia ter referências mencionado em outros do que as linguagens Java]

Este é como perguntar “, o que é esta obsessão com instruções da CPU? Eu perder alguma coisa por não aspersão instruções x86 MOV todo o lugar?”

Você só necessidade ponteiros durante a programação em um nível baixo. Na maioria das implementações de linguagem de programação de alto nível, os ponteiros são usados ??apenas como extensivamente como em C, mas escondido do usuário pelo compilador.

Então ... não se preocupe. Você está usando ponteiros já - e sem os perigos de fazê-lo incorretamente, também. :)

Eu vejo ponteiros como uma transmissão manual em um carro. Se você aprender a dirigir com um carro que tem uma transmissão automática, que não vai fazer para um mau condutor. E você ainda pode fazer quase tudo que os motoristas que aprenderam em uma transmissão manual pode fazer. Não será apenas um buraco em seu conhecimento de condução. Se você tivesse que dirigir um manual, você provavelmente estaria em apuros. Claro, é fácil entender o conceito básico de que, mas uma vez que você tem que fazer um arranque em subida, você está ferrado. Mas, ainda há um lugar para transmissões manuais. Por exemplo, os motoristas de carros de corrida precisa ser capaz de mudar para pegar o carro para responder de forma mais adequada para as condições de corrida atuais. Ter uma transmissão manual é muito importante para o seu sucesso.

Este é muito semelhante à programação agora. Há uma necessidade para o desenvolvimento de C / C ++ no mesmo software. Alguns exemplos são high-end 3D jogos, software de baixo nível incorporado, coisas onde a velocidade é uma parte crítica da finalidade do software, e uma linguagem de nível inferior que permite o acesso mais próximo aos dados reais que precisa ser processada é a chave para que o desempenho . No entanto, para a maioria dos programadores não é esse o caso e não saber ponteiros não é incapacitante. No entanto, eu acredito que todos podem beneficiar aprendendo sobre C e ponteiros, e transmissões manuais também.

Uma vez que você tenha sido programação em linguagens orientadas a objeto, deixe-me colocar desta forma.

Você começa Objeto A instanciar objeto B, e você passá-lo como um parâmetro de método para Object C. Os modifica objeto C alguns valores no objeto B. Quando você está de volta ao objeto de código de A, você pode ver o valor alterado em objeto B. Por que isso acontece?

Porque você passou em um referência do objeto B para objeto C, não fez outra cópia do objeto B. Então Objeto A e objeto C ambas as referências de espera para o mesmo objeto B na memória. Muda de um lugar e ser visto em outro. Isso é chamado por referência.

Agora, se você usar tipos primitivos vez disso, como int ou float, e passá-los como parâmetros do método, as mudanças no objeto C não pode ser visto por Objeto A, porque Objeto A apenas passado um cópia em vez de uma referência de sua própria cópia da variável. Isso é chamado pelo valor.

Você provavelmente já sabia disso.

Voltando à linguagem C, Função A passa a Função B algumas variáveis. Estes parâmetros de função são cópias nativamente, por valor. A fim de que a Função B para manipular a cópia pertencente a função A, a Função A deve passar um ponteiro para a variável, de modo que torna-se uma passagem por referência.

"Ei, aqui está o endereço de memória ao meu variável inteiro. Coloque o novo valor naquele local endereço e eu vou pegar mais tarde."

Observe o conceito é semelhante, mas não 100% análoga. Os ponteiros podem fazer muito mais do que apenas passar "por referência". Ponteiros permitem funções para manipular locais arbitrários de memória para o valor necessário. Ponteiros também são usados ??para apontar para novos endereços de código de execução para executar dinamicamente lógica arbitrária, não apenas variáveis ??de dados. Ponteiros podem até apontar para outros ponteiros (ponteiro duplo). Isso é poderoso, mas também muito fácil de introduzir difíceis de detectar bugs e vulnerabilidades de segurança.

Se você ainda não viu ponteiros antes, você está certamente perdendo este mini-gem:

void strcpy(char *dest, char *src)
{    
        while(*dest++ = *src++);
}

Historicamente, o que fez a programação possível era a percepção de que posições de memória poderia prender instruções de computador, e não apenas de dados.

Ponteiros surgiu a partir da constatação de que posições de memória também pode armazenar o endereço de outros locais de memória, dando-nos assim engano. Sem ponteiros (em um nível baixo) estruturas de dados mais complicada seria impossível. Não vinculados-listas, binário-árvores ou de hash-tables. Sem passar por referência, apenas pelo valor. Desde ponteiros podem apontar para código, sem eles nós também não teria funções virtuais ou função de olhar para cima tabelas.

Eu uso ponteiros e referências fortemente no meu dia a dia de trabalho ... em código gerenciado (C #, Java) e não gerenciados (C ++, C). Eu aprendi sobre como lidar com ponteiros eo que eles estão pelo próprio mestre ... [Binky !!] [1] necessidades Nada mais a ser dito;)

A diferença entre um ponteiro e de referência é a seguinte. Um ponteiro é um endereço para algum bloco de memória. Ele pode ser reescrito ou em outras palavras, transferido para algum outro bloco de memória. Uma referência é simplesmente uma mudança de nome de um objecto. Ela só pode ser atribuído uma vez! Uma vez que é atribuído a um objeto, não pode ser atribuído a outro. A referência não é um endereço, é outro nome para a variável. Confira C ++ FAQ para saber mais sobre isso.

Link1

link2

Atualmente estou a cintura na concepção de alguns software empresarial de alto nível em que blocos de dados (armazenados em um banco de dados SQL, neste caso) são referenciados por 1 ou mais outras entidades. Se um pedaço de dados permanece quando há mais entidades referenciá-lo, estamos armazenamento desperdício. Se alguns pontos de referência para que os dados que não está presente, isso é um grande problema também.

Há uma forte analogia a ser feita entre os nossos problemas, e os de gerenciamento de memória em uma linguagem que ponteiros usos. É extremamente útil para ser capaz de falar com os meus colegas, em termos de que a analogia. Não exclusão de dados sem referência é um "vazamento de memória". Uma referência que não sai do lugar é um "ponteiro pendente". Podemos escolher "liberta" explícitas, ou podemos implementar "coleta de lixo" usando "contagem de referência".

Então, aqui, gerenciamento de memória de baixo nível entendimento está ajudando aplicações de alto nível de design.

Em Java você está usando ponteiros o tempo todo. A maioria das variáveis ??são ponteiros para objetos - e é por isso:

StringBuffer x = new StringBuffer("Hello");
StringBuffer y = x;
x.append(" boys");
System.out.println(y);

... imprime "Olá meninos" e não "Olá".

A única diferença em C é que é comum adicionar e subtrair ponteiros -. E se você começar errado lógica você pode acabar mexendo com dados que você não deve tocar

Strings são fundamentais para C (e outras línguas relacionadas). Ao programar em C, você deve gerenciar sua memória. Você não pode simplesmente dizer "tudo bem, eu vou precisar de um monte de cordas"; você precisa pensar sobre a estrutura de dados. A quantidade de memória que você precisa? Quando você vai alocá-lo? Quando você vai libertá-la? Vamos dizer que você quer 10 cordas, cada uma com um máximo de 80 caracteres.

Ok, cada corda é um array de caracteres (81 caracteres - você não deve esquecer o nulo ou você vai se arrepender!) E, em seguida, cada corda é em si mesmo em uma matriz. O resultado final será uma matriz multidimensional algo como

char dict[10][81];

Nota, aliás, que dict não é uma "string" ou um "array", ou um "char". É um ponteiro. Quando você tenta imprimir uma dessas cordas, tudo o que você está fazendo é passar o endereço de um único caractere; C assume que, se ele simplesmente começa a imprimir caracteres que acabará por atingir um valor nulo. E ele assume que se você está no início de uma corda, e você saltar para a frente 81 bytes, você vai estar no início da próxima string. E, de fato levando o ponteiro e acrescentando 81 bytes a ele é o única maneira possível para saltar para a próxima corda.

Então, por que são ponteiros importante? Porque você não pode fazer nada sem eles. Você não pode mesmo fazer algo simples como imprimir um monte de cordas; você certeza não pode fazer nada interessante como implementar ligada listas ou hashes, ou filas, ou árvores, ou um sistema de arquivos, ou algum código de gerenciamento de memória, ou um kernel ou ... o que quer. Você precisa entender deles, porque C apenas lhe entrega um bloco de memória e permite que você faça o resto, e fazer qualquer coisa com um bloco de memória requer ponteiros.

Além disso, muitas pessoas sugerem que a capacidade de entender ponteiros correlaciona altamente com habilidade de programação. Joel fez este argumento, entre outros. Por exemplo

Agora, eu admito livremente que a programação com ponteiros não é necessário em 90% do código escrito hoje, e, na verdade, é francamente perigoso no código de produção. ESTÁ BEM. Isso é bom. E programação funcional apenas não é muito usado na prática. Concordou.

Mas ainda é importante para alguns dos mais trabalhos de programação emocionantes. Sem ponteiros, por exemplo, você nunca será capaz de trabalhar no kernel do Linux. Você não pode entender uma linha de código em Linux, ou, na verdade, qualquer sistema operacional, sem ponteiros realmente entender.

A partir aqui . Excelente artigo.

Para ser honesto, os desenvolvedores mais experientes terão uma risada (espero amigável) se você não sabe ponteiros. No meu trabalho anterior, que tivemos duas novas contratações no ano passado (recém-formado) que não sabia sobre ponteiros, e que só foi o tema de conversa com eles por cerca de uma semana. Ninguém podia acreditar como alguém poderia se formar sem saber ponteiros ...

As referências no C ++ são fundamentalmente diferentes de referências em linguagens Java ou .NET; linguagens .NET têm tipos especiais chamados de "byrefs", que se comportem como C ++ "referências".

A C ++ referência ou .NET byref (vou usar o último termo, de distinguir de referências NET) é um tipo especial que não detenha uma variável, mas sim contém informações suficientes para identificar uma variável (ou algo que podem comportar-se como um, tal como uma ranhura de matriz) realizada noutro local. Byrefs são geralmente utilizados apenas como parâmetros Função / argumentos, e destinam-se a ser efémera. Código da qual passa uma byref para uma função garantias de que a variável que é identificado, assim, existirão pelo menos até que função retorna, e funções geralmente garantir a não manter qualquer cópia de um byref depois voltam (nota que em C ++ a última restrição não é Forçados). Assim, byrefs não pode sobreviver as variáveis ??identificadas desse modo.

Em Java e .NET línguas, uma referência é um tipo que identifica um objeto do montão; cada objeto pilha tem uma classe associada, eo código em dados de acesso classe pode do objeto pilha armazenados no objeto. objetos heap pode conceder código fora limitado ou pleno acesso aos dados nele armazenados, e / ou permitir que o código de fora para chamar certos métodos dentro de sua classe. Usando uma referência para chamar um método de sua classe fará com que a referência a ser disponibilizados para esse método, que pode usá-lo para acessar dados (mesmo dados privados) dentro do objeto heap.

O que faz referências especial em Java e .NET idiomas é que eles mantêm, como uma invariante absoluta, que cada referência não nula continuará a identificar o mesmo objeto pilha enquanto existir essa referência. Uma vez que nenhuma referência a um objeto pilha existe em qualquer lugar do universo, o objeto pilha simplesmente deixarão de existir, mas não há nenhuma maneira um objeto pilha pode deixar de existir enquanto qualquer referência a ele existe, nem há qualquer maneira de um "normal "referência a um objeto heap para tornar-se espontaneamente outra coisa senão uma referência a esse objeto. Java e .NET Do têm tipos especiais "de referência fraca", mas mesmo eles defender o invariante. Se há referências não-fracas para um qualquer lugar objeto existir no universo, então quaisquer referências fracas existentes serão invalidados; uma vez que ocorre, não haverá quaisquer referências ao objeto e pode, assim, ser invalidado.

ponteiros, como ambas as referências C ++ e Java / .NET referências, identificar objetos, mas ao contrário dos tipos de referências acima mencionadas podem sobreviver ao objetos eles identificam. Se o objeto identificado por um ponteiro deixa de existir, mas o ponteiro em si não, qualquer tentativa de usar o ponteiro irá resultar em um comportamento indefinido. Se um ponteiro não é conhecido, quer ser null ou para identificar um objeto que existe presentemente, não há nenhuma maneira padrão definido para fazer qualquer com esse ponteiro diferente de substituí-lo com outra coisa. É perfeitamente legítimo que um ponteiro para continuar a existir depois que o objeto identificado, assim, deixou de fazê-lo, desde que nada nunca usa o ponteiro, mas é necessário que algo fora do ponteiro indicar se é ou não é seguro de usar porque não há nenhuma maneira de pedir o ponteiro em si.

A principal diferença entre ponteiros e referências (de qualquer tipo) é que as referências podem sempre ser perguntado se eles são válidos (que vai ser tanto válido ou identificável como null), e se observado para ser válida eles permanecerão assim como desde que eles existem. Ponteiros não podem ser perguntado se eles são válidos, eo sistema irá fazer nada para assegurar que os ponteiros não se torna inválida, nem permitir ponteiros que se tornam inválida a ser reconhecido como tal.

Por um longo tempo eu não entendia ponteiros, mas eu entendi variedade de endereçamento. Então, eu costumo colocar juntos alguma área de armazenamento para objetos em uma matriz, em seguida, usar um índice para essa matriz como o conceito 'ponteiro'.

SomeObject store[100];
int a_ptr = 20;
SomeObject A = store[a_ptr];

Um problema com esta abordagem é que depois eu modifiquei 'A', eu teria de atribuí-lo para a matriz 'loja' para que as alterações sejam permanentes:

store[a_ptr] = A;

Nos bastidores, a linguagem de programação estava fazendo várias cópias-operações. Na maioria das vezes isso não afetou o desempenho. É feito na maior parte do código e repetitivo propenso a erros.

Depois que eu aprendi a entender ponteiros, eu me afastei de implementar a matriz de endereçamento abordagem. A analogia é ainda bastante válido. Basta considerar que a matriz 'loja é gerida por tempo de execução da linguagem de programação.

SomeObject A;
SomeObject* a_ptr = &A;
// Any changes to a_ptr's contents hereafter will affect
// the one-true-object that it addresses. No need to reassign.

Hoje em dia, eu só usar ponteiros quando eu não posso copiar legitimamente um objeto. Há um monte de razões pelas quais isso pode ser o caso:

  1. Para evitar um objeto-cópia caro operação por causa de desempenho.
  2. algum outro fator não permite uma operação objeto-cópia.
  3. Você quer uma chamada de função para ter efeitos secundários sobre um objecto (não fazer passar o objecto, passar o ponteiro ao mesmo).
  4. Em alguns idiomas- se você quiser retornar mais de um valor a partir de um função (embora geralmente evitado).

Os ponteiros são a forma mais pragmática de representar engano em linguagens de programação de baixo nível.

Os ponteiros são importantes! Eles "point" para um endereço de memória, e muitas estruturas internas são representados como ponteiros, isto é, um conjunto de cordas é realmente uma lista de ponteiros para ponteiros! Ponteiros também pode ser usado para as variáveis ??de atualização passados ??para as funções.

Você precisa deles se você quer gerar "objetos" em tempo de execução sem pré alocar memória na pilha

Parâmetro Efficency - passando um apontador (Int - 4 bytes). Em oposição à cópia de um objecto inteiro (arbitrariamente grande)

classes Java são passados ??via de referência (basicamente um ponteiro) também btw, é só que em java que está escondida do programador.

Programação em linguagens como C e C ++ você está muito mais perto do "metal". Ponteiros manter uma posição de memória onde suas variáveis, dados, funções etc. ao vivo. Você pode passar um ponteiro em torno em vez de passar por valor (copiar suas variáveis ??e dados).

Há duas coisas que são difíceis com ponteiros:

  1. Os ponteiros sobre ponteiros, abordando, etc. pode ficar muito enigmática. Isso leva a erros, e é difícil de ler.
  2. Memória que ponteiros apontam para é muitas vezes atribuídos a partir da pilha, o que significa que você é responsável por liberar a memória. Bigger sua aplicação fica, mais difícil é manter-se com este requisito e você acabar com vazamentos de memória que são difíceis de rastrear.

Você poderia comparar o comportamento ponteiro para como os objetos Java são passados ??ao redor, com a ressalva de que em Java você não precisa se preocupar em liberar a memória como isso é tratado pela coleta de lixo. Dessa forma você obtém as coisas boas sobre ponteiros, mas não têm de lidar com os negativos. Você ainda pode obter vazamentos de memória em Java, claro, se você não fizer isso de referência seus objetos, mas isso é uma questão diferente.

Também apenas algo a nota, você pode usar ponteiros em C # (ao contrário de referências normais), marcando um bloco de código como inseguros. Então você pode correr ao redor endereços de memória de mudança diretamente e fazer aritmética de ponteiro e todas essas coisas divertidas. É ótimo para manipulação de imagens muito rápido (o único lugar que eu pessoalmente tenho usado isso).

Tanto quanto eu sei Java e ActionScript não suportam código inseguro e ponteiros.

Estou sempre aflito com o foco em coisas como ponteiros ou referências em linguagens de alto nível. É realmente útil pensar em um nível mais alto de abstração em termos do comportamento de objetos (ou mesmo apenas funções) ao invés de pensar em termos de "deixe-me ver, se eu enviar o endereço desta coisa para lá, então essa coisa vai me retornar um ponteiro para outra coisa "

Considere até mesmo uma função swap simples. Se você tiver

void swap (int & a, int & b)

ou

Processo de Troca (var a, b: inteiro)

então interpretar estes para significar que os valores podem ser alterados. O fato de que este está a ser implementado, passando os endereços das variáveis ??é apenas uma distração do fim.

Mesmo com objetos --- não pensam em identificadores de objetos como ponteiros ou referências a "coisas". Em vez disso, basta pensar nelas como, bem, objetos, para o qual você pode enviar mensagens. Mesmo em línguas primitivas, como C ++, você pode ir muito mais longe muito mais rápido pelo pensamento (e escrever) em como alto nível de uma possível.

gravar mais de 2 linhas de C ou C ++ e você vai descobrir.

Eles são "ponteiros" para o local de memória de uma variável. É como passar uma variável por referência tipo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top