Qual a diferença entre uma referência Java e um ponteiro C?

97

C tem ponteiros e Java tem o que é chamado de referências. Eles têm algumas coisas em comum no sentido de que todos apontam para alguma coisa. Eu sei que os ponteiros em C armazenam os endereços para os quais eles apontam. A referência também armazena o endereço? Como eles são diferentes, exceto que o ponteiro é mais flexível e propenso a erros?

Gnijuohz
fonte
10
note Observação: O C ++ também possui referências diferentes de ponteiros ou referências a java
jk.
@jk. Eu pensei que seria o mesmo que em Java. Qual é a diferença?
Gnijuohz 28/03
17
As referências C ++ não podem ser reativadas (ou seja, você não pode alterar o objeto designado) e não são anuláveis ​​(ou seja, não é possível que elas validamente não façam referência a nenhum objeto).
AProgramador
@Gnijuohz Reformulando o que o @AProgrammer disse: Uma finalreferência em Java é quase equivalente a uma referência em C ++. O motivo pelo qual eles não são exatamente equivalentes é que uma referência C ++ também não é anulável, enquanto uma finalreferência em Java é anulável.
Utku

Respostas:

142

As referências podem ser implementadas armazenando o endereço. Geralmente, as referências Java serão implementadas como ponteiros, mas isso não é exigido pela especificação. Eles podem estar usando uma camada adicional de indireção para facilitar a coleta de lixo. Mas no final (quase sempre) se resume a ponteiros (no estilo C) envolvidos na implementação de referências (no estilo Java).

Você não pode fazer aritmética de ponteiro com referências. A diferença mais importante entre um ponteiro em C e uma referência em Java é que você não pode realmente obter (e manipular) o valor subjacente de uma referência em Java. Em outras palavras: você não pode fazer aritmética de ponteiro.

Em C, você pode adicionar algo a um ponteiro (ou seja, o endereço) ou subtrair algo para apontar para coisas que estão "próximas" ou para lugares que estão em qualquer lugar.

Em Java, uma referência aponta para uma coisa e apenas essa coisa. Você pode fazer uma variável manter uma referência diferente , mas não pode simplesmente pedir que aponte para "a coisa após a coisa original".

As referências são fortemente digitadas. Outra diferença é que o tipo de uma referência é muito mais rigorosamente controlado em Java do que o tipo de um ponteiro em C. Em C, você pode ter um int*e convertê-lo em um char*e apenas reinterpretar a memória nesse local. Essa reinterpretação não funciona em Java: você só pode interpretar o objeto na outra extremidade da referência como algo que já é (ou seja, você pode converter uma Objectreferência em Stringreferência apenas se o objeto apontado for realmente a String).

Essas diferenças tornam os ponteiros C mais poderosos, mas também mais perigosos. Essas duas possibilidades (aritmética do ponteiro e reinterpretação dos valores apontados) adicionam flexibilidade ao C e são a fonte de parte do poder da linguagem. Mas eles também são grandes fontes de problemas, porque, se usados ​​incorretamente, podem facilmente quebrar as suposições de que seu código foi criado. E é muito fácil usá-los incorretamente.

Joachim Sauer
fonte
18
+1 para poder . Não confie nos detalhes da implementação.
a CVn
2
+1 A coleta de lixo não merece menção como um ponto específico em negrito? É uma outra maneira que ponteiros C são mais poderosos, mas também mais perigoso (risco de ponteiros oscilantes a memória liberada causando corrupção de memória, o risco de vazamentos de memória)
MarkJ
Outra diferença entre referências e ponteiros é que um ponteiro em C pode ser convertido em uma sequência de números (por exemplo, usando memcpypara mover um para a char[]) e vice-versa. Se um ponteiro é convertido em uma sequência de números que é armazenada em algum lugar (talvez mostrado na tela e copiado pelo operador em um pedaço de papel), todas as cópias do ponteiro no computador são destruídas e essa sequência de números é convertida de volta a um ponteiro (talvez depois de ser digitado pelo operador), o ponteiro ainda deve apontar para a mesma coisa que fazia antes. Um programa que ...
supercat
... exibia um ponteiro como números e depois convertia números inseridos manualmente em um ponteiro, poderia ser "mau", mas não invocaria nenhum Comportamento Indefinido, a menos que o operador digitasse números que não foram mostrados para formar um válido ponteiro. Portanto, a coleta de lixo de uso geral é impossível no C totalmente portátil, porque não há como o computador saber se existe uma cópia de um ponteiro em algum lugar do universo.
supercat
De acordo com o JLS, §4.3.1, as referências em Java são ponteiros; portanto, "geralmente as referências Java serão implementadas como ponteiros, mas isso não é exigido pela especificação". é falso.
Lew Bloch
8

Referências C ++ são diferentes novamente.

Eles precisam ser inicializados e não podem ser nulos (pelo menos não em um programa bem formado) e não podem ser recolocados para se referir a outra coisa. uma referência C ++ é muito mais parecida com um alias para um objeto.

Outra diferença importante entre ponteiros e referências Java / C ++ é que você pode pegar o endereço de um ponteiro que não pode acessar o endereço de uma referência (na verdade, uma referência C ++ não precisa realmente existir como um objeto na memória); consequentemente, você pode ter um ponteiro para um ponteiro, mas não uma referência a uma referência

jk.
fonte
4

Referências Java e ponteiros C diferem exatamente em dois pontos:

  1. Não há aritmética para o primeiro.
  2. E você não pode criar uma referência Java para o que quiser, só pode copiar aqueles salvos em algum lugar acessível (campos estáticos, campos de objetos, variáveis ​​locais) ou retornados por invocações de funções (como chamadas de construtor), que, portanto, se referem a Java objetos (nunca tipos básicos como referências, char, inte assim por diante).

Alguém escreveu que as referências são fortemente tipadas, porque você não pode forçar o compilador a tratar um int*como a char*.
Completamente aparte do fato de que essa conversão específica é realmente segura , não há polimorfismo em C, de modo que a comparação não é iniciada.
Certamente, Java é mais fortemente tipado que C, não que esse seja um recurso de ponteiros C versus referências a Java; você precisa usar o JNI para quebrar a segurança de tipo (além de desconsiderar as restrições genéricas), mas mesmo em C você deve forçar o compilador.

Alguém escreveu que as referências Java pode ser implementado como ponteiros C, a que eu digo com certeza, como eles são estritamente menos poderosos, em máquinas 32Bit eles normalmente são, se o JVM é implementado em C . Embora em máquinas de 64 bits, eles normalmente são ponteiros de objetos comuns compactados ("OOPs compactados") para economizar espaço e largura de banda.
De qualquer forma, esses ponteiros C também não precisam ser equivalentes aos endereços de hardware, mesmo que normalmente (> 99% das implementações) sejam por motivos de desempenho.
Finalmente, esse é um detalhe de implementação que não é exposto ao programador.

Desduplicador
fonte
-1

Eles são um pouco diferentes. Em Java, uma cópia da referência é copiada para a pilha de uma função chamada, apontando para o mesmo objeto que a função de chamada e permitindo que você manipule esse objeto. No entanto, você não pode alterar o objeto ao qual a função de chamada se refere.

Considere o seguinte código java

public static void changeRValue(StringBuffer sb){
    sb = new StringBuffer("helllllo"); /*attempt to assign the reference
                                        to a new object*/
}
public static void main(String[] args) {
    StringBuffer sb = new StringBuffer("hi");     //Create a new string buffer
    changeRValue(sb);                             //Call changeRValue
    System.out.println(sb.toString());            //Prints "hi" not "hello"
}

Agora considere um ponteiro em c ++:

void func(Dog* dog){
    *dog = Dog("hello world"); //Change the value of dog to a new object
}

int main(int argc, const char * argv[]) {
    Dog dog1("hi");                            //Create a dog object
    func(&dog1);                               //pass the address of dog
    cout << dog1.name;                         //Prints "hello world" not hi.
    return 0;
}
Eladian
fonte
Eu pensei que eu poderia acrescentar que isso poderia ser visto como semelhante a um const Dog *
Eladian
2
Você pode modificar o objeto apontado em Java com a mesma facilidade que em C ++. Você só precisa ter acesso aos membros certos. Os objetos Java não possuem operadores de designação porque o Java não suporta sobrecarga de operador definida pelo usuário; portanto, você não pode usar um operador sobrecarregado, grande coisa. Essa falta de Java nada tem a ver com o fato de que as referências Java são as mesmas que os ponteiros C ++ sem a aritmética dos ponteiros.
Deduplicator