En quoi une référence Java est-elle différente d'un pointeur C?

97

C a des pointeurs et Java a ce qu’on appelle des références. Ils ont certaines choses en commun dans le sens où ils indiquent tous quelque chose. Je sais que les pointeurs en C stockent les adresses qu’ils pointent. Est-ce que la référence stocke aussi l'adresse? En quoi sont-ils différents, si ce n'est que le pointeur est plus flexible et sujet aux erreurs?

Gnijuohz
la source
10
note C ++ a également des références différentes des pointeurs ou des références java
jk.
@jk. Je pensais que ce serait la même chose qu'en Java. Quelle est la différence?
Gnijuohz
17
Les références C ++ ne sont pas ré-indexables (vous ne pouvez pas modifier l'objet désigné) et ne peuvent pas être annulées (vous ne pouvez pas les faire valablement référencer aucun objet).
AProgrammer
@Gnijuohz Pour reformuler ce que @AProgrammer a déclaré: Une finalréférence en Java est presque équivalente à une référence en C ++. La raison pour laquelle ils ne sont pas exactement équivalents est qu’une référence C ++ n’est pas non plus nullable, alors qu’une finalréférence en Java est nulle.
Utku

Réponses:

142

Les références peuvent être implémentées en stockant l'adresse. Habituellement, les références Java sont implémentées en tant que pointeurs, mais cela n'est pas requis par la spécification. Ils peuvent utiliser une couche supplémentaire d'indirection pour permettre un ramassage des ordures plus facile. Mais au final, cela se résumera (presque toujours) à des pointeurs (de style C) impliqués dans la mise en œuvre de références (de style Java).

Vous ne pouvez pas faire de l'arithmétique de pointeur avec des références. La différence la plus importante entre un pointeur en C et une référence en Java est qu’il est impossible d’obtenir (et de manipuler) la valeur sous-jacente d’une référence en Java. En d'autres termes: vous ne pouvez pas faire de l'arithmétique de pointeur.

En C, vous pouvez ajouter quelque chose à un pointeur (c'est-à-dire l'adresse) ou soustraire quelque chose pour pointer sur des choses "proches" ou sur des endroits situés à n'importe quel endroit.

En Java, une référence pointe vers une chose et cette chose seulement. Vous pouvez faire en sorte qu'une variable contienne une référence différente , mais vous ne pouvez pas lui demander simplement de désigner "la chose après la chose d'origine".

Les références sont fortement typées. Une autre différence est que le type d'une référence est beaucoup plus strictement contrôlé en Java que le type d'un pointeur est en C. En C, vous pouvez avoir un int*et le convertir en un char*et simplement réinterpréter la mémoire à cet emplacement. Cette réinterprétation ne fonctionne pas en Java: vous pouvez uniquement interpréter l’objet à l’autre extrémité de la référence comme quelque chose qu’il est déjà (c’est-à-dire que vous pouvez transformer une Objectréférence en Stringréférence uniquement si l’objet pointé est réellement un String).

Ces différences rendent les pointeurs C plus puissants, mais aussi plus dangereux. Ces deux possibilités (arithmétique de pointeur et réinterprétation des valeurs pointées) ajoutent de la flexibilité à C et sont à l'origine d'une partie de la puissance du langage. Mais ils constituent également une source importante de problèmes, car s'ils ne sont pas utilisés correctement, ils peuvent facilement briser les hypothèses selon lesquelles votre code est construit. Et c'est assez facile de les utiliser incorrectement.

Joachim Sauer
la source
18
+1 pour la puissance . Ne comptez pas sur les détails de la mise en œuvre.
un CVn
2
+1 Le ramassage des ordures ne mérite-t-il pas d'être mentionné comme un point audacieux? C'est une autre façon de rendre les pointeurs C plus puissants, mais aussi plus dangereux (risque de points pendants vers la mémoire libérée, ce qui entraîne une corruption de la mémoire, risque de fuite de mémoire)
MarkJ
Une autre différence entre les références et les pointeurs réside dans le fait qu’un pointeur en C peut être converti en une suite de nombres (par exemple, en memcpydéplaçant un dans un char[]) et inversement. Si un pointeur est converti en une séquence de nombres qui est stockée quelque part (peut-être affiché à l'écran et copié par l'opérateur sur un bout de papier), toutes les copies du pointeur de l'ordinateur sont détruites et cette séquence de nombres est convertie. Pour revenir à un pointeur (peut-être après avoir été saisi par l'opérateur), le pointeur doit toujours indiquer la même chose que précédemment. Un programme qui ...
Supercat
... affiché un pointeur sous forme de nombres, puis converti les nombres entrés manuellement en un pointeur, pourrait être "diabolique", mais il ne pourrait invoquer aucun comportement indéfini à moins que l'opérateur n'entre des nombres n'ayant pas été montrés comme formant un code valide. aiguille. La récupération de place à des fins générales est donc impossible en C entièrement portable, car l'ordinateur ne peut absolument pas savoir si une copie d'un pointeur peut exister quelque part dans l'univers.
Supercat
Selon JLS, §4.3.1, les références en Java sont des pointeurs. Par conséquent, "les références Java sont généralement implémentées en tant que pointeurs, mais ce n'est pas requis par la spécification." c'est faux.
Lew Bloch
8

Les références C ++ sont encore différentes.

Ils doivent être initialisés et ne peuvent pas être nuls (du moins pas dans un programme bien formé) et ne peuvent pas être replacés pour faire référence à autre chose. une référence C ++ ressemble beaucoup plus à un alias d'objet.

Une autre différence importante entre les pointeurs et les références Java / C ++ est que vous pouvez utiliser l'adresse d'un pointeur auquel vous ne pouvez pas accéder (par exemple, une référence C ++ ne doit pas exister sous la forme d'un objet en mémoire). pointeur vers un pointeur mais pas une référence à une référence

jk.
la source
4

Les références Java et les pointeurs C diffèrent par exactement deux points:

  1. Il n'y a pas de pointeur-arithmétique pour le premier.
  2. Et vous ne pouvez pas créer une référence Java vers ce que vous voulez, vous pouvez uniquement copier ceux sauvegardés dans un endroit accessible (champs statiques, champs d'objets, variables locales) ou renvoyé par des appels de fonction (comme des appels de constructeur), qui font donc tous référence à Java. objets (jamais à des types de base comme des références, char, intetc.).

Quelqu'un a écrit que les références sont fortement typées, car vous ne pouvez pas forcer le compilateur à traiter un int*comme char*.
Complètement à part le fait que cette conversion particulière est réellement sûre , il n'y a pas de polymorphisme en C, de sorte que la comparaison est un non-démarreur.
Certes, Java est plus typé que C, bien que ce ne soit pas une caractéristique des pointeurs C ni des références Java, vous devez utiliser le JNI pour casser la sécurité de type (mis à part le non respect des restrictions génériques), mais même en C, vous devez forcer. le compilateur.

Quelqu'un a écrit que les références Java peuvent être mises en œuvre en tant que pointeurs C, auquel je dis que, comme ils sont strictement moins puissants, sur les machines 32Bit ils sont généralement, si la machine virtuelle Java est implémenté en C . Bien que sur les machines 64 bits, ce sont normalement des pointeurs d’objets ordinaires ("POO compressés") compressés pour économiser de l’espace et de la bande passante.
Quoi qu'il en soit, ces pointeurs C ne doivent pas non plus être équivalents aux adresses matérielles, même s'ils sont généralement (> 99% des implémentations) pour des raisons de performances.
Enfin, il s’agit d’un détail d’implémentation qui n’est pas exposé au programmeur.

Déduplicateur
la source
-1

Ils sont légèrement différents. En Java, une copie de la référence est copiée dans la pile d'une fonction appelée, pointant sur le même objet que la fonction appelante et vous permettant de manipuler cet objet. Cependant, vous ne pouvez pas modifier l'objet auquel la fonction appelante fait référence.

Considérons le code Java suivant

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"
}

Considérons maintenant un pointeur en 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
la source
Je pensais pouvoir ajouter qu'il pourrait être perçu comme similaire à un chien
constant
2
Vous pouvez modifier l'objet pointé en Java avec la même facilité qu'en C ++. Vous devez juste avoir accès aux bons membres. Les objets Java n'ont pas d'opérateur d'affectation, car Java ne prend pas en charge la surcharge définie par l'utilisateur. Vous ne pouvez donc pas utiliser un opérateur surchargé. Ce manque de Java n'a rien à voir avec le fait que les références Java sont les mêmes que les pointeurs C ++ sans arithmétique de pointeur.
Deduplicator