Même les langues où vous avez une manipulation de pointeur explicite, comme C, sont toujours passées par valeur (vous pouvez les passer par référence mais ce n'est pas le comportement par défaut).
Quel est l'avantage de cela, pourquoi tant de langues sont-elles passées par des valeurs et pourquoi d'autres sont-elles passées par référence ? (Je comprends que Haskell est passé par référence bien que je ne sois pas sûr).
programming-languages
language-design
Mon code n'a pas de bugs
la source
la source
void acceptEntireProgrammingLanguageByValue(C++);
temp := a; a := b; b := temp;
Et quand elle reviendra, les valeurs dea
etb
seront échangées. Il n'y a aucun moyen de faire ça en C; vous devez faire passer des pointeurs versa
etb
et laswap
routine doit agir sur les valeurs auxquelles ils renvoient.Réponses:
Passer par valeur est souvent plus sûr que passer par référence, car vous ne pouvez pas modifier accidentellement les paramètres de votre méthode / fonction. Cela simplifie l'utilisation du langage, puisque vous n'avez pas à vous soucier des variables que vous attribuez à une fonction. Vous savez qu'ils ne seront pas changés et c'est souvent ce à quoi vous vous attendez .
Toutefois, si vous souhaitez modifier les paramètres, vous devez avoir une opération explicite pour rendre cela clair (passer un pointeur). Cela forcera tous vos appelants à effectuer l'appel légèrement différemment (
&variable
en C), ce qui explicite le fait que le paramètre de variable peut être modifié.Alors maintenant, vous pouvez supposer qu'une fonction ne changera pas votre paramètre de variable, sauf si cela est explicitement marqué (en vous obligeant à passer un pointeur). Il s’agit d’une solution plus sûre et plus propre que l’alternative: supposons que tout puisse modifier vos paramètres, à moins qu’ils ne le disent spécifiquement.
la source
Call-by-value et call-by-reference sont des techniques de mise en œuvre qui avaient été confondues avec les modes de passage de paramètres il y a longtemps.
Au début, il y avait Fortran. FORTRAN ne disposait que d’appels par référence, car les sous-programmes devaient pouvoir modifier leurs paramètres et les cycles de calcul étaient trop coûteux pour permettre plusieurs modes de passage de paramètres.
ALGOL a proposé appel par nom et appel par valeur. Call-by-value était pour des choses qui n'étaient pas supposées être changées (paramètres d'entrée). Appel par nom était pour les paramètres de sortie. Call-by-name s'est avéré être une marmite majeure, et ALGOL 68 l'a abandonné.
PASCAL a fourni appel par valeur et appel par référence. Cela ne permettait pas au programmeur d'indiquer au compilateur qu'il transmettait un objet volumineux (généralement un tableau) par référence, afin d'éviter de détruire la pile de paramètres, mais que l'objet ne devait pas être modifié.
PASCAL a ajouté des pointeurs au lexique de conception de langage.
C a fourni appel par valeur et simulé appel par référence en définissant un opérateur kludge pour renvoyer un pointeur sur un objet arbitraire en mémoire.
Les langues suivantes ont copié le C, principalement parce que les concepteurs n’avaient jamais rien vu d’autre. C'est probablement pourquoi appel par valeur est si populaire.
C ++ a ajouté un kludge au-dessus du C kludge pour fournir un appel par référence.
Maintenant, en tant que résultat direct appel par valeur vs appel par référence vs appel par pointeur-kludge, C et C ++ (programmeurs) ont des maux de tête horribles avec les pointeurs const et les pointeurs vers const (lecture seule) objets.
Ada a réussi à éviter tout ce cauchemar.
Ada n'a pas explicitement appel par valeur vs appel par référence. Au lieu de cela, Ada a dans les paramètres (qui peuvent être lus mais non écrits), les paramètres out (qui DOIVENT être écrits avant de pouvoir être lus) et les paramètres out, qui peuvent être lus et écrits dans n'importe quel ordre. Le compilateur décide si un paramètre particulier est passé par valeur ou par référence: il est transparent pour le programmeur.
la source
In the beginning, there was FORTRAN
.0
préfixe est incohérent avec tous les autres préfixes non-base dix. Cela peut être un peu excusable en C (et la plupart des langages compatibles avec les sources, par exemple C ++, Objective C), si octal était la seule base alternative lors de son introduction, mais lorsque les langages plus modernes utilisent les deux0x
et0
dès le début, cela leur donne simplement un aspect médiocre. pensé.Passer par référence permet d’obtenir de très subtils effets secondaires non désirés qu'il est très difficile, voire impossible, de retracer lorsqu'ils commencent à provoquer le comportement imprévu.
Pass par valeur, en particulier
final
,static
ouconst
les paramètres d’entrée font disparaître toute cette classe de bugs.Les langages immuables sont encore plus déterministes et plus faciles à raisonner et à comprendre ce qui se passe et ce qui devrait sortir d'une fonction.
la source
Swap
hacks qui n'utilisent pas de variable temporaire, bien qu'ils ne soient pas susceptibles de poser problème eux-mêmes, montrent à quel point un exemple plus complexe peut être difficile à déboguer.Le point essentiel de la division de gros programmes en petits sous-programmes est que vous pouvez raisonner indépendamment des sous-programmes. Le passage par référence casse cette propriété. (Tout comme l'état mutable partagé.)
En fait, C est toujours passé par valeur, jamais par référence. Vous pouvez prendre une adresse de quelque chose et la transmettre, mais l'adresse sera toujours transmise par valeur.
L’utilisation de référence par référence a deux raisons principales:
Personnellement, je pense que # 1 est faux: c'est presque toujours une excuse pour la mauvaise conception d'API et / ou de langage:
Vous pouvez également simuler plusieurs valeurs de retour en les regroupant dans une structure de données légère telle qu'un tuple. Cela fonctionne particulièrement bien si le langage prend en charge la correspondance de modèle ou la liaison de déstructuration. Par exemple Ruby:
Souvent, vous n'avez même pas besoin de plusieurs valeurs de retour. Par exemple, l'un des modèles courants est que le sous-programme renvoie un code d'erreur et que le résultat réel est réécrit par référence. Au lieu de cela, vous devriez simplement lever une exception si l'erreur est inattendue ou renvoyer un
Either<Exception, T>
si l'erreur est attendue. Une autre méthode consiste à renvoyer un booléen indiquant si l'opération a réussi et à renvoyer le résultat réel par référence. De nouveau, si l’échec est inattendu, vous devriez plutôt émettre une exception. Si l’échec est attendu, par exemple lors de la recherche d’une valeur dans un dictionnaire, vous devez renvoyer unMaybe<T>
.Le passage par référence peut s'avérer plus efficace que le passage par valeur, car vous n'avez pas à copier la valeur.
Non, Haskell n'est pas référencé. Ce n'est pas non plus valeur par passe. Pass-by-reference et pass-by-value sont deux stratégies d'évaluation strictes, mais Haskell n'est pas stricte.
En fait, la spécification Haskell ne spécifie aucune stratégie d'évaluation particulière. La plupart des implémentations Hakell utilisent une combinaison d'appel par nom et d'appel par besoin (une variante de l'appel par nom avec memoization), mais la norme ne l'exige pas.
Notez que même pour les langages stricts, il n’a pas de sens de faire la distinction entre pass-by-reference et pass-by-value pour les langages fonctionnels, car la différence entre eux ne peut être observée que si vous modifiez la référence. Par conséquent, l'implémenteur est libre de choisir entre les deux, sans casser la sémantique du langage.
la source
Le comportement varie en fonction du modèle d'appel du langage, du type d'arguments et des modèles de mémoire du langage.
Avec les types natifs simples, le passage par valeur vous permet de transmettre la valeur par les registres. Cela peut être très rapide car la valeur n'a pas besoin d'être chargée de la mémoire ni sauvegardée. Une optimisation similaire est également possible simplement en réutilisant la mémoire utilisée par l'argument une fois que l'appelé est terminé, sans risquer de gâcher la copie de l'objet de l'appelant. Si l'argument était un objet temporaire, vous en auriez probablement enregistré une copie complète (C ++ 11 rend ce type d'optimisation encore plus évident avec la nouvelle référence de droite et sa sémantique de déplacement).
Dans de nombreux langages OO (C ++ est plus une exception dans ce contexte), vous ne pouvez pas transmettre d'objet par valeur. Vous êtes obligé de le transmettre par référence. Cela rend le code polymorphe par défaut et est plus en ligne avec la notion d'instances propre à OO. De plus, si vous voulez passer par valeur, vous devez en faire une copie vous-même, en reconnaissant le coût de la performance qui a généré cette action. Dans ce cas, la langue choisit pour vous l’approche la plus susceptible de vous donner les meilleures performances.
Dans le cas de langages fonctionnels, je suppose que passer par valeur ou référence est simplement une question d’optimisation. Étant donné que les fonctions de ce langage sont pures et donc exempt d’effets secondaires, il n’ya vraiment aucune raison de copier la valeur, sauf pour la vitesse. Je suis même presque sûr que ce langage partage souvent la même copie d'objets avec les mêmes valeurs, une possibilité disponible uniquement si vous utilisiez une sémantique de référence pass (const). Python a également utilisé cette astuce pour les chaînes entières et communes (comme les méthodes et les noms de classe), en expliquant pourquoi les entiers et les chaînes sont des objets constants en Python. Cela permet également d’optimiser à nouveau le code, en permettant par exemple une comparaison de pointeur au lieu de comparaison de contenu, et une évaluation paresseuse de certaines données internes.
la source
Vous pouvez passer une expression par valeur, c'est naturel. Passer une expression par référence (temporaire) est ... bizarre.
la source
Si vous passez par référence, alors vous travaillez toujours avec des valeurs globales, avec tous les problèmes des globals (à savoir la portée et les effets secondaires non voulus).
Les références, tout comme les globales, sont parfois bénéfiques, mais elles ne devraient pas être votre premier choix.
la source